diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cd3efe..829306f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# V0.2.1 + +- Renew rollup config +- Added runtim plugin for babel + # V0.2.0 - Added esbuild feature for build typescript diff --git a/jest.config.ts b/jest.config.ts index 657b9f8..7d88f03 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -5,7 +5,7 @@ const config: Config.InitialOptions = { verbose: true, preset: 'ts-jest', testEnvironment: 'node', - coveragePathIgnorePatterns: ['/node_modules/', '/test/', '__test__', 'dist'], + coveragePathIgnorePatterns: ['/node_modules/', '/tests/', '__tests__', 'dist'], collectCoverageFrom: ['src/**/*.{js,ts}'] } diff --git a/package.json b/package.json index e34ddbc..054e699 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "fs-extra": "^9.1.0", "inquirer": "^8.0.0", "lint-staged": "^10.5.4", + "lodash.camelcase": "^4.3.0", "ora": "^5.4.0", "prettier": "^2.2.1", "tslib": "^2.2.0", @@ -59,6 +60,7 @@ "@types/glob": "^7.1.3", "@types/inquirer": "^7.3.1", "@types/jest": "^26.0.22", + "@types/lodash.camelcase": "^4.3.6", "@types/node": "^14.14.41", "@types/prettier": "^2.2.3", "@types/yargs": "^16.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79d7cdf..ee9040a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,7 @@ specifiers: '@types/glob': ^7.1.3 '@types/inquirer': ^7.3.1 '@types/jest': ^26.0.22 + '@types/lodash.camelcase': ^4.3.6 '@types/node': ^14.14.41 '@types/prettier': ^2.2.3 '@types/yargs': ^16.0.1 @@ -28,6 +29,7 @@ specifiers: inquirer: ^8.0.0 jest: ^26.6.3 lint-staged: ^10.5.4 + lodash.camelcase: ^4.3.0 ora: ^5.4.0 prettier: ^2.2.1 pretty-quick: ^3.1.0 @@ -45,6 +47,7 @@ dependencies: fs-extra: 9.1.0 inquirer: 8.0.0 lint-staged: 10.5.4 + lodash.camelcase: 4.3.0 ora: 5.4.0 prettier: 2.2.1 tslib: 2.2.0 @@ -58,6 +61,7 @@ devDependencies: '@types/glob': 7.1.3 '@types/inquirer': 7.3.1 '@types/jest': 26.0.22 + '@types/lodash.camelcase': 4.3.6 '@types/node': 14.14.41 '@types/prettier': 2.2.3 '@types/yargs': 16.0.1 @@ -1130,6 +1134,22 @@ packages: } dev: true + /@types/lodash.camelcase/4.3.6: + resolution: + { + integrity: sha512-hd/TEuPd76Jtf1xEq85CHbCqR+iqvs5IOKyrYbiaOg69BRQgPN9XkvLj8Jl8rBp/dfJ2wQ1AVcP8mZmybq7kIg== + } + dependencies: + '@types/lodash': 4.14.168 + dev: true + + /@types/lodash/4.14.168: + resolution: + { + integrity: sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q== + } + dev: true + /@types/minimatch/3.0.4: resolution: { @@ -4789,6 +4809,10 @@ packages: p-locate: 5.0.0 dev: true + /lodash.camelcase/4.3.0: + resolution: { integrity: sha1-soqmKIorn8ZRA1x3EfZathkDMaY= } + dev: false + /lodash.clonedeep/4.5.0: resolution: { integrity: sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= } dev: true diff --git a/src/features/babel/__tests__/babel.spec.ts b/src/features/babel/__tests__/babel.spec.ts new file mode 100644 index 0000000..01f3712 --- /dev/null +++ b/src/features/babel/__tests__/babel.spec.ts @@ -0,0 +1,101 @@ +import path from 'path' +import fs from 'fs-extra' +import { isSkip, setup } from '../index' +import { DependencyType, getDepsCollection, clearDeps } from '../../../core/dependency' +import { BUILD_TOOLS } from '../../typescript/build-tools' + +const rootPath = path.resolve(__dirname, 'tmp') +describe('As chore eslint feature', () => { + beforeEach(async () => { + clearDeps() + await fs.ensureDir(rootPath) + }) + + afterEach(async () => { + await fs.remove(rootPath) + }) + + it('should skip install this feature when project has exists babel config file', async () => { + const eslintPath = path.resolve(rootPath, '.babelrc') + await fs.ensureFile(eslintPath) + const context = { rootPath, answers: {} } + const skiped = await isSkip(context) + expect(skiped).toBe(true) + }) + + it('should skip this feature when given special build tool', async () => { + const skiped = await isSkip({ rootPath, answers: { buildTool: BUILD_TOOLS.TSC } }) + expect(skiped).toBe(true) + + const skiped1 = await isSkip({ rootPath, answers: { buildTool: BUILD_TOOLS.ESBUILD } }) + expect(skiped1).toBe(true) + + const skiped2 = await isSkip({ rootPath, answers: { buildTool: BUILD_TOOLS.ROLLUP } }) + expect(skiped2).toBe(false) + + const skiped3 = await isSkip({ rootPath, answers: { buildTool: BUILD_TOOLS.WEBPACK } }) + expect(skiped3).toBe(false) + }) + + it('should write babel config to project root when setup has been called with no react', async () => { + const babelrcPath = path.resolve(rootPath, '.babelrc') + const context = { rootPath, answers: {} } + await setup(context) + + const configContent = await fs.readJSON(babelrcPath) + expect(configContent).toStrictEqual({ + presets: ['@babel/preset-env', '@babel/preset-typescript'], + plugins: [ + '@babel/plugin-transform-runtime', + '@babel/proposal-class-properties', + '@babel/proposal-object-rest-spread' + ] + }) + + const deps = getDepsCollection() + const expectedDependencies = [ + '@babel/cli', + '@babel/core', + '@babel/preset-env', + '@babel/plugin-proposal-class-properties', + '@babel/plugin-proposal-object-rest-spread', + '@babel/preset-typescript', + '@babel/plugin-transform-runtime' + ].map(item => ({ name: item, type: DependencyType.DEV })) + + expectedDependencies.push({ name: '@babel/runtime', type: DependencyType.DEFAULT }) + + expect(deps).toStrictEqual(expectedDependencies) + }) + + it('should write babel config to project root when setup has been called with react needed', async () => { + const eslintPath = path.resolve(rootPath, '.babelrc') + const context = { rootPath, answers: { isReactNeeded: true } } + await setup(context) + + const configContent = await fs.readJSON(eslintPath) + expect(configContent).toStrictEqual({ + presets: ['@babel/preset-env', '@babel/preset-typescript', '@babel/preset-react'], + plugins: [ + '@babel/plugin-transform-runtime', + '@babel/proposal-class-properties', + '@babel/proposal-object-rest-spread' + ] + }) + + const deps = getDepsCollection() + const expectedDependencies = [ + '@babel/cli', + '@babel/core', + '@babel/preset-env', + '@babel/plugin-proposal-class-properties', + '@babel/plugin-proposal-object-rest-spread', + '@babel/preset-typescript', + '@babel/plugin-transform-runtime' + ].map(item => ({ name: item, type: DependencyType.DEV })) + + expectedDependencies.push({ name: '@babel/runtime', type: DependencyType.DEFAULT }) + expectedDependencies.push({ name: '@babel/preset-react', type: DependencyType.DEV }) + expect(deps).toStrictEqual(expectedDependencies) + }) +}) diff --git a/src/features/babel/index.ts b/src/features/babel/index.ts index 58f044a..0550973 100644 --- a/src/features/babel/index.ts +++ b/src/features/babel/index.ts @@ -1,11 +1,17 @@ import type { FeatureSetup, IsSkipFeature } from '../../types' import { resolve } from 'path' import { BUILD_TOOLS } from '../typescript/build-tools' -import { addDevDeps } from '../../core/dependency' +import { addDep, addDevDeps } from '../../core/dependency' import { rederTemplate } from '../../core/template' +import { fileExists } from '../../utils/path_helper' -export const isSkip: IsSkipFeature = async ({ answers }) => { - return [BUILD_TOOLS.TSC, BUILD_TOOLS.ESBUILD].includes(answers.buildTool) +const configFileExists = async (path: string) => await fileExists(resolve(path, '.babelrc')) + +export const isSkip: IsSkipFeature = async ({ rootPath, answers }) => { + return ( + (await configFileExists(rootPath)) || + [BUILD_TOOLS.TSC, BUILD_TOOLS.ESBUILD].includes(answers.buildTool) + ) } export const setup: FeatureSetup = async context => { @@ -18,9 +24,12 @@ export const setup: FeatureSetup = async context => { '@babel/preset-env', '@babel/plugin-proposal-class-properties', '@babel/plugin-proposal-object-rest-spread', - '@babel/preset-typescript' + '@babel/preset-typescript', + '@babel/plugin-transform-runtime' ]) + addDep('@babel/runtime') + if (isReactNeeded) { addDevDeps(['@babel/preset-react']) } diff --git a/src/features/babel/templates/.babelrc.tpl b/src/features/babel/templates/.babelrc.tpl index ffc808d..f2a06e5 100644 --- a/src/features/babel/templates/.babelrc.tpl +++ b/src/features/babel/templates/.babelrc.tpl @@ -9,6 +9,7 @@ <% } %> ], "plugins": [ + "@babel/plugin-transform-runtime", "@babel/proposal-class-properties", "@babel/proposal-object-rest-spread" ] diff --git a/src/features/boilerplate/templates/index.spec.ts.tpl b/src/features/boilerplate/templates/index.spec.ts.tpl index 620b3b6..ccbaf95 100644 --- a/src/features/boilerplate/templates/index.spec.ts.tpl +++ b/src/features/boilerplate/templates/index.spec.ts.tpl @@ -1,5 +1,5 @@ -import sum from '../src/index'; +import sum from '../src/index' test('sum should return 2 when given 1 1', () => { - expect(sum(1, 1)).toBe(2); + expect(sum(1, 1)).toBe(2) }) diff --git a/src/features/boilerplate/templates/index.ts.tpl b/src/features/boilerplate/templates/index.ts.tpl index c2d3d62..9713396 100644 --- a/src/features/boilerplate/templates/index.ts.tpl +++ b/src/features/boilerplate/templates/index.ts.tpl @@ -1,3 +1,3 @@ - export default function sum(a: number, b: number) { - return a + b; - } +export default function sum(a: number, b: number) { + return a + b +} diff --git a/src/features/esbuild/__test__/esbuild.spec.ts b/src/features/esbuild/__tests__/esbuild.spec.ts similarity index 100% rename from src/features/esbuild/__test__/esbuild.spec.ts rename to src/features/esbuild/__tests__/esbuild.spec.ts diff --git a/src/features/eslint/__test__/eslint.spec.ts b/src/features/eslint/__tests__/eslint.spec.ts similarity index 100% rename from src/features/eslint/__test__/eslint.spec.ts rename to src/features/eslint/__tests__/eslint.spec.ts diff --git a/src/features/rollup/__tests__/rollup.spec.ts b/src/features/rollup/__tests__/rollup.spec.ts new file mode 100644 index 0000000..f61a43b --- /dev/null +++ b/src/features/rollup/__tests__/rollup.spec.ts @@ -0,0 +1,54 @@ +import path from 'path' +import fs from 'fs-extra' +import { isSkip, setup } from '../index' +import { DependencyType, getDepsCollection, clearDeps } from '../../../core/dependency' +import { BUILD_TOOLS } from '../../typescript/build-tools' + +const rootPath = path.resolve(__dirname, 'tmp') +describe('As chore eslint feature', () => { + beforeEach(async () => { + clearDeps() + await fs.ensureDir(rootPath) + }) + + afterEach(async () => { + await fs.remove(rootPath) + }) + + it('should skip install this feature when project has exists rollup config file', async () => { + const rollupConfigPath = path.resolve(rootPath, 'rollup.config.ts') + await fs.ensureFile(rollupConfigPath) + const context = { rootPath, answers: {} } + const skiped = await isSkip(context) + expect(skiped).toBe(true) + }) + + it('should skip this feature when given special build tool', async () => { + const skiped = await isSkip({ rootPath, answers: { buildTool: BUILD_TOOLS.TSC } }) + expect(skiped).toBe(true) + + const skiped1 = await isSkip({ rootPath, answers: { buildTool: BUILD_TOOLS.ESBUILD } }) + expect(skiped1).toBe(true) + + const skiped2 = await isSkip({ rootPath, answers: { buildTool: BUILD_TOOLS.ROLLUP } }) + expect(skiped2).toBe(false) + + const skiped3 = await isSkip({ rootPath, answers: { buildTool: BUILD_TOOLS.WEBPACK } }) + expect(skiped3).toBe(true) + }) + + it('should write rollup config to project root when setup has been called', async () => { + const context = { rootPath, answers: {} } + await setup(context) + + const deps = getDepsCollection() + const expectedDependencies = [ + 'rollup', + '@rollup/plugin-babel', + '@rollup/plugin-node-resolve', + '@rollup/plugin-commonjs' + ].map(item => ({ name: item, type: DependencyType.DEV })) + + expect(deps).toStrictEqual(expectedDependencies) + }) +}) diff --git a/src/features/rollup/index.ts b/src/features/rollup/index.ts index 4c32558..955c54f 100644 --- a/src/features/rollup/index.ts +++ b/src/features/rollup/index.ts @@ -1,5 +1,6 @@ import type { FeatureSetup, IsSkipFeature } from '../../types' import { resolve } from 'path' +import camelCase from 'lodash.camelcase' import { BUILD_TOOLS } from '../typescript/build-tools' import { addDevDeps } from '../../core/dependency' import { rederTemplate } from '../../core/template' @@ -13,18 +14,23 @@ export const isSkip: IsSkipFeature = async ({ rootPath, answers }) => { } export const setup: FeatureSetup = async context => { - const { rootPath } = context + const { + rootPath, + answers: { packageName } + } = context addDevDeps([ 'rollup', '@rollup/plugin-babel', '@rollup/plugin-node-resolve', - '@rollup/plugin-commonjs', - 'rollup-plugin-sourcemaps' + '@rollup/plugin-commonjs' ]) await rederTemplate( resolve(rootPath, 'rollup.config.ts'), - resolve(__dirname, './templates/rollup.config.ts.tpl') + resolve(__dirname, './templates/rollup.config.ts.tpl'), + { + appName: camelCase(packageName) + } ) } diff --git a/src/features/rollup/templates/rollup.config.ts.tpl b/src/features/rollup/templates/rollup.config.ts.tpl index 67f2704..99520a6 100644 --- a/src/features/rollup/templates/rollup.config.ts.tpl +++ b/src/features/rollup/templates/rollup.config.ts.tpl @@ -1,7 +1,6 @@ import commonjs from '@rollup/plugin-commonjs' -import { nodeResolve } from '@rollup/plugin-node-resolve' +import resolve from '@rollup/plugin-node-resolve' import babel from '@rollup/plugin-babel' -import sourceMaps from 'rollup-plugin-sourcemaps' import pkg from './package.json' export default { @@ -16,20 +15,22 @@ export default { plugins: [ // Allows node_modules resolution // https://github.com/rollup/rollup-plugin-node-resolve#usage - nodeResolve(), + resolve(), // Allow bundling cjs modules. Rollup doesn't understand cjs - commonjs(), + commonjs({ include: 'node_modules/**' }), // Compile TypeScript/JavaScript files - babel({ extensions: ['.js', '.jsx', '.es6', '.es', '.mjs', '.ts', '.tsx'], include: ['src/**/*'], babelHelpers: 'runtime' }), - - // Resolve source maps to the original source - sourceMaps() + babel({ + extensions: ['.ts', '.tsx', '.js', '.jsx', '.es6', '.es', '.mjs'], + include: ['src/**/*'], + exclude: 'node_modules/**', + babelHelpers: 'runtime' + }) ], output: [ - { file: pkg.main, name: '', format: 'umd', sourcemap: true }, + { file: pkg.main, name: '<%= appName %>', format: 'umd', sourcemap: true }, { file: pkg.module, format: 'es', sourcemap: true } ] } diff --git a/src/features/typescript/build-tools.ts b/src/features/typescript/build-tools.ts index ba810af..a010154 100644 --- a/src/features/typescript/build-tools.ts +++ b/src/features/typescript/build-tools.ts @@ -27,6 +27,6 @@ export const buildTools = () => { name: 'buildTool', message: '🛠 Which build tool do you want to use?', choices, - default: BUILD_TOOLS.ESBUILD + default: BUILD_TOOLS.TSC } } diff --git a/src/features/typescript/index.ts b/src/features/typescript/index.ts index c281e69..520b4b7 100644 --- a/src/features/typescript/index.ts +++ b/src/features/typescript/index.ts @@ -2,7 +2,7 @@ import type { FeatureSetup, IsSkipFeature, QuestionBuilder } from '../../types' import { resolve } from 'path' import { fileExists } from '../../utils/path_helper' import { rederTemplate } from '../../core/template' -import { buildTools } from './build-tools' +import { buildTools, BUILD_TOOLS } from './build-tools' import { addDep, addDevDeps } from '../../core/dependency' export const questionBuilder: QuestionBuilder = async () => { @@ -16,7 +16,7 @@ export const isSkip: IsSkipFeature = async context => { export const setup: FeatureSetup = async context => { const { rootPath, - answers: { isReactNeeded } + answers: { isReactNeeded, buildTool } } = context addDep('tslib') @@ -26,7 +26,8 @@ export const setup: FeatureSetup = async context => { resolve(rootPath, 'tsconfig.json'), resolve(__dirname, './templates/tsconfig.json.tpl'), { - isReactNeeded + isReactNeeded, + useRoolup: buildTool === BUILD_TOOLS.ROLLUP } ) } diff --git a/src/features/typescript/templates/tsconfig.json.tpl b/src/features/typescript/templates/tsconfig.json.tpl index 9b00425..7016c4f 100644 --- a/src/features/typescript/templates/tsconfig.json.tpl +++ b/src/features/typescript/templates/tsconfig.json.tpl @@ -11,11 +11,11 @@ // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ - <% if (isReactNeeded) { %> + <%_ if (isReactNeeded) { -%> "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ - <% } else { %> + <%_ } else { -%> // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ - <% } %> + <%_ } -%> // "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */ @@ -29,6 +29,9 @@ "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + <%_ if (useRoolup) { -%> + "resolveJsonModule": true, + <%_ } -%> /* Strict Type-Checking Options */ "strict": true, /* Enable all strict type-checking options. */