Skip to content

Commit

Permalink
feat(create-abell): add bun template, and option to skip dep installa…
Browse files Browse the repository at this point in the history
…tion (#172)
  • Loading branch information
saurabhdaware authored Oct 29, 2023
1 parent c84bbf3 commit 04661a5
Show file tree
Hide file tree
Showing 17 changed files with 436 additions and 51 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@
"abell:test": "cd packages/abell && pnpm test",
"abell:test:once": "pnpm --filter abell test:once",
"create-abell": "pnpm --filter create-abell scaffold",
"create-abell:test:once": "pnpm --filter create-abell test:once",
"docs:dev": "pnpm abell:build && pnpm --filter docs dev",
"docs:generate": "pnpm --filter docs generate",
"docs:generate:ci": "pnpm abell:build && pnpm install && pnpm docs:generate",
"packages:test:once": "pnpm --filter \"./packages/**\" test:once",
"playground:test": "cd playground && vitest",
"playground:test:once": "pnpm --filter \"./playground/**\" test:once",
"test:once": "pnpm abell:build && pnpm playground:test:once && pnpm packages:test:once",
"test:once": "pnpm abell:build && pnpm playground:test:once && pnpm packages:test:once && pnpm create-abell:test:once",
"// Linting Stuff Starts from Here": "you mostly won't have to care about these",
"eslint": "eslint \"packages/**/**/**/*.{ts,js}\"",
"prettier": "prettier --write \"packages/**/**/**/*.[jt]s\" \"playground/**/**/**/*.[jt]s\"",
Expand Down
129 changes: 129 additions & 0 deletions packages/create-abell/src/__tests__/create.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/* eslint-disable max-len */
import fs from 'fs';
import path from 'path';
import { spawn } from 'child_process';
import {
afterAll,
afterEach,
beforeAll,
beforeEach,
describe,
expect,
test,
vi
} from 'vitest';
import create from '../create';
import { deleteDir, windowsifyCommand } from '../utils.js';

const testUtilsDir = path.join(__dirname, 'test-utils', 'scaffolds');

vi.mock('child_process');

// Shoutout to chatgpt for this function
const makeTree = (directoryPath: string, indent = 0) => {
const files = fs
.readdirSync(directoryPath)
// sorting to make directory read sequence same
.sort();

let tree = '';

files.forEach((file) => {
const filePath = path.join(directoryPath, file);
const stats = fs.statSync(filePath);
const isDirectory = stats.isDirectory();

tree += ' '.repeat(indent * 2);
tree += isDirectory ? '├── ' + file + '\n' : '└── ' + file + '\n';

if (isDirectory) {
tree += makeTree(filePath, indent + 1);
}
});

return tree;
};

beforeAll(() => {
deleteDir(testUtilsDir);
});

afterAll(() => {
deleteDir(testUtilsDir);
});

describe('create', () => {
beforeEach(() => {
vi.spyOn(process, 'cwd').mockReturnValue(testUtilsDir);
// @ts-expect-error: only mocking what is used
spawn.mockImplementation(() => {
const mockChildProcess = {
stdout: { on: vi.fn() }, // Mock stdout behavior if needed
stderr: { on: vi.fn() }, // Mock stderr behavior if needed
on: vi.fn()
};
return mockChildProcess;
});
});

afterEach(() => {
vi.resetAllMocks();
deleteDir(path.join(testUtilsDir, 'hello-abell'));
});

test('should create project with default template', async () => {
await create('hello-abell', {
installer: 'skip',
template: 'default'
});

expect(spawn).not.toBeCalled();
expect(fs.existsSync(path.join(testUtilsDir, 'hello-abell'))).toBe(true);
expect(makeTree(path.join(testUtilsDir, 'hello-abell')))
.toMatchInlineSnapshot(`
"├── _components
└── global.meta.abell
└── navbar.abell
└── about.abell
├── client-assets
└── global.css
└── index-client.js
└── config.js
└── index.abell
└── package.json
├── public
└── abell-logo.svg
"
`);
});

test('should create project with default template and bun installer', async () => {
await create('hello-abell', {
installer: 'bun',
template: 'default'
});

const expectedCommand = 'bun install';
const commandArgs = windowsifyCommand(expectedCommand).split(' ');

expect(spawn).toBeCalledWith(commandArgs[0], [commandArgs[1]], {
cwd: path.join(testUtilsDir, 'hello-abell'),
stdio: 'inherit'
});

const packageJSONPath = path.join(
testUtilsDir,
'hello-abell',
'package.json'
);
const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, 'utf-8'));

expect(packageJSON.name).toBe('hello-abell');
expect(packageJSON.scripts).toMatchInlineSnapshot(`
{
"dev": "bunx --bun abell dev",
"generate": "bunx --bun abell generate",
}
`);
});
});
38 changes: 35 additions & 3 deletions packages/create-abell/src/__tests__/steps.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable max-len */
import path from 'path';
import { describe, test, expect } from 'vitest';
import { setNameInPackageJSON } from '../steps';
import { setPackageJSONValues } from '../steps';

describe('setNameInPackageJSON', () => {
test('should set the name in package.json', () => {
Expand All @@ -11,12 +11,44 @@ describe('setNameInPackageJSON', () => {
'test-package.json'
);
const randomPackageName = `test-package-123`;
setNameInPackageJSON(packageJSONPath, randomPackageName);
setPackageJSONValues(packageJSONPath, { name: randomPackageName });
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJSONContent = require(packageJSONPath);
expect(packageJSONContent.name).toBe(randomPackageName);
expect(packageJSONContent.version).toBe('0.0.15');
expect(packageJSONContent.description).toBe('test-description');
setNameInPackageJSON(packageJSONPath, 'unset');
setPackageJSONValues(packageJSONPath, { name: 'unset' });
});

test('should set dev scripts in package.json', () => {
const packageJSONPath = path.join(
__dirname,
'test-utils',
'test-package.json'
);
const randomPackageName = `test-package-123`;
setPackageJSONValues(packageJSONPath, {
name: randomPackageName,
scripts: {
dev: 'bunx --bun abell dev',
generate: 'bunx --bun abell generate'
}
});
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJSONContent = require(packageJSONPath);
expect(packageJSONContent.name).toBe(randomPackageName);
expect(packageJSONContent.scripts.dev).toBe('bunx --bun abell dev');
expect(packageJSONContent.scripts.generate).toBe(
'bunx --bun abell generate'
);
expect(packageJSONContent.version).toBe('0.0.15');
expect(packageJSONContent.description).toBe('test-description');
setPackageJSONValues(packageJSONPath, {
name: 'unset',
scripts: {
dev: 'abell dev',
generate: 'abell generate'
}
});
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"name": "unset",
"version": "0.0.15",
"description": "test-description"
"description": "test-description",
"scripts": {
"dev": "abell dev",
"generate": "abell generate"
}
}
68 changes: 43 additions & 25 deletions packages/create-abell/src/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@ import {
getProjectInfo,
getTemplate,
scaffoldTemplate,
setNameInPackageJSON
setPackageJSONValues
} from './steps';
import { colors, deleteDir, log, relative, run } from './utils';
import {
colors,
deleteDir,
log,
packageManagerRunMap,
relative,
run
} from './utils';

export type CreateAbellOptions = {
installer?: 'npm' | 'yarn' | 'pnpm' | 'bun';
installer?: 'npm' | 'yarn' | 'pnpm' | 'bun' | 'skip';
template?: string;
};

Expand Down Expand Up @@ -37,39 +44,50 @@ async function create(
template
});

let runCommand: string | undefined;

console.log('');
log.info(`Running ${colors.bold(installCommand)}`, 2);
// 3. Install Dependencies
try {
await run(installCommand, {
cwd: projectPath
});
} catch (err) {
log.failure(`Could not install dependencies. Skipping ${installCommand}`);

if (installCommand) {
// 3. Install Dependencies
log.info(`Running ${colors.bold(installCommand)}`, 2);
try {
await run(installCommand, {
cwd: projectPath
});
} catch (err) {
log.failure(`Could not install dependencies. Skipping ${installCommand}`);
}

runCommand = packageManagerRunMap[installCommand];
}

// 4. Set name in project's package.json
setNameInPackageJSON(`${projectPath}/package.json`, projectDisplayName);
setPackageJSONValues(`${projectPath}/package.json`, {
name: projectDisplayName,
scripts: {
dev:
installCommand === 'bun install' ? 'bunx --bun abell dev' : 'abell dev',
generate:
installCommand === 'bun install'
? 'bunx --bun abell generate'
: 'abell generate'
}
});

// 5. Delete `.git` (For projects scaffolded from github)
deleteDir(`${projectPath}/.git`);

// 6. Log Success
log.success(`${projectDisplayName} scaffolded successfully 🚀\n`);
let runCommand = 'npm run dev';
if (installCommand === 'yarn') {
runCommand = 'yarn dev';
} else if (installCommand === 'pnpm install') {
runCommand = 'pnpm run dev';
} else if (installCommand === 'bun install') {
runCommand = 'bun run dev';
}

log.info(
`${colors.bold(`cd ${relProjectPath}`)} and run ${colors.bold(
runCommand
)} to run the dev-server\n`
);
if (runCommand) {
log.info(
`${colors.bold(`cd ${relProjectPath}`)} and run ${colors.bold(
runCommand
)} to run the dev-server\n`
);
}
}

export default create;
Loading

0 comments on commit 04661a5

Please sign in to comment.