From 642eeb863e8bc3253816f36d51e0cb9bdc728a7b Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Tue, 31 Dec 2019 18:46:03 -0500 Subject: [PATCH] (refactor): split commands into separate directories - should be easier to read & understand the codebase as there's a better separation of concerns now - don't need to look at / concern oneself with build code when editing lint code, for instance - should also make future refactors much easier, as no need to parse or change a big file with all the commands and logic - also split up the command logic from the action logic as two separate functions for each command --- src/{ => build}/babelPluginTsdx.ts | 0 src/{ => build}/createBuildConfigs.ts | 4 +- src/{ => build}/createProgressEstimator.ts | 4 +- src/{ => build}/createRollupConfig.ts | 10 +- src/{ => build}/errors/evalToString.ts | 0 src/{ => build}/errors/extractErrors.ts | 10 +- src/{ => build}/errors/invertObject.ts | 0 .../errors/transformErrorMessages.ts | 6 +- src/build/index.ts | 297 +++++++++ src/build/types.ts | 50 ++ src/{ => create}/getInstallArgs.ts | 0 src/{ => create}/getInstallCmd.ts | 0 src/create/index.ts | 204 ++++++ src/{ => create}/messages.ts | 9 +- src/{ => create}/output.ts | 0 src/{ => create}/templates/basic.ts | 0 src/{ => create}/templates/index.ts | 0 .../templates/react-with-storybook.ts | 0 src/{ => create}/templates/react.ts | 0 src/{ => create}/templates/template.d.ts | 0 src/{ => create}/templates/utils/index.ts | 0 src/files.ts | 12 + src/index.ts | 607 +----------------- src/{ => lint}/createEslintConfig.ts | 5 +- src/lint/index.ts | 76 +++ src/{ => test}/createJestConfig.ts | 0 src/test/index.ts | 56 ++ src/types.ts | 51 -- src/utils.ts | 1 + 29 files changed, 735 insertions(+), 667 deletions(-) rename src/{ => build}/babelPluginTsdx.ts (100%) rename src/{ => build}/createBuildConfigs.ts (98%) rename src/{ => build}/createProgressEstimator.ts (90%) rename src/{ => build}/createRollupConfig.ts (98%) rename src/{ => build}/errors/evalToString.ts (100%) rename src/{ => build}/errors/extractErrors.ts (97%) rename src/{ => build}/errors/invertObject.ts (100%) rename src/{ => build}/errors/transformErrorMessages.ts (99%) create mode 100644 src/build/index.ts create mode 100644 src/build/types.ts rename src/{ => create}/getInstallArgs.ts (100%) rename src/{ => create}/getInstallCmd.ts (100%) create mode 100644 src/create/index.ts rename src/{ => create}/messages.ts (99%) rename src/{ => create}/output.ts (100%) rename src/{ => create}/templates/basic.ts (100%) rename src/{ => create}/templates/index.ts (100%) rename src/{ => create}/templates/react-with-storybook.ts (100%) rename src/{ => create}/templates/react.ts (100%) rename src/{ => create}/templates/template.d.ts (100%) rename src/{ => create}/templates/utils/index.ts (100%) create mode 100644 src/files.ts rename src/{ => lint}/createEslintConfig.ts (93%) create mode 100644 src/lint/index.ts rename src/{ => test}/createJestConfig.ts (100%) create mode 100644 src/test/index.ts diff --git a/src/babelPluginTsdx.ts b/src/build/babelPluginTsdx.ts similarity index 100% rename from src/babelPluginTsdx.ts rename to src/build/babelPluginTsdx.ts diff --git a/src/createBuildConfigs.ts b/src/build/createBuildConfigs.ts similarity index 98% rename from src/createBuildConfigs.ts rename to src/build/createBuildConfigs.ts index b53bdee54..6923414d6 100644 --- a/src/createBuildConfigs.ts +++ b/src/build/createBuildConfigs.ts @@ -2,9 +2,9 @@ import { RollupOptions, OutputOptions } from 'rollup'; import * as fs from 'fs-extra'; import { concatAllArray } from 'jpjs'; -import { paths } from './constants'; -import { TsdxOptions, NormalizedOpts } from './types'; +import { paths } from '../constants'; +import { TsdxOptions, NormalizedOpts } from './types'; import { createRollupConfig } from './createRollupConfig'; // check for custom tsdx.config.js diff --git a/src/createProgressEstimator.ts b/src/build/createProgressEstimator.ts similarity index 90% rename from src/createProgressEstimator.ts rename to src/build/createProgressEstimator.ts index 8962945eb..0381878e3 100644 --- a/src/createProgressEstimator.ts +++ b/src/build/createProgressEstimator.ts @@ -1,6 +1,8 @@ -import { paths } from './constants'; import util from 'util'; import mkdirp from 'mkdirp'; + +import { paths } from '../constants'; + const progressEstimator = require('progress-estimator'); export async function createProgressEstimator() { diff --git a/src/createRollupConfig.ts b/src/build/createRollupConfig.ts similarity index 98% rename from src/createRollupConfig.ts rename to src/build/createRollupConfig.ts index a1ce7d4e8..a6d760aae 100644 --- a/src/createRollupConfig.ts +++ b/src/build/createRollupConfig.ts @@ -1,5 +1,4 @@ -import { safeVariableName, safePackageName, external } from './utils'; -import { paths } from './constants'; +import * as fs from 'fs-extra'; import { RollupOptions } from 'rollup'; import { terser } from 'rollup-plugin-terser'; import { DEFAULT_EXTENSIONS } from '@babel/core'; @@ -10,10 +9,13 @@ import replace from '@rollup/plugin-replace'; import resolve from '@rollup/plugin-node-resolve'; import sourceMaps from 'rollup-plugin-sourcemaps'; import typescript from 'rollup-plugin-typescript2'; + +import { paths } from '../constants'; +import { safeVariableName, safePackageName, external } from '../utils'; + +import { TsdxOptions } from './types'; import { extractErrors } from './errors/extractErrors'; import { babelPluginTsdx } from './babelPluginTsdx'; -import { TsdxOptions } from './types'; -import * as fs from 'fs-extra'; const errorCodeOpts = { errorMapFilePath: paths.appErrorsJson, diff --git a/src/errors/evalToString.ts b/src/build/errors/evalToString.ts similarity index 100% rename from src/errors/evalToString.ts rename to src/build/errors/evalToString.ts diff --git a/src/errors/extractErrors.ts b/src/build/errors/extractErrors.ts similarity index 97% rename from src/errors/extractErrors.ts rename to src/build/errors/extractErrors.ts index d5fb00ce0..5bbf81469 100644 --- a/src/errors/extractErrors.ts +++ b/src/build/errors/extractErrors.ts @@ -7,11 +7,13 @@ import fs from 'fs-extra'; import * as babylon from 'babylon'; import traverse from 'babel-traverse'; +import pascalCase from 'pascal-case'; + +import { paths } from '../../constants'; +import { safeVariableName } from '../../utils'; + import { invertObject } from './invertObject'; import { evalToString } from './evalToString'; -import { paths } from '../constants'; -import { safeVariableName } from '../utils'; -import pascalCase from 'pascal-case'; const babylonOptions = { sourceType: 'module', @@ -111,7 +113,7 @@ function ErrorDev(message) { return error; } -export default ErrorDev; +export default ErrorDev; `, 'utf-8' ); diff --git a/src/errors/invertObject.ts b/src/build/errors/invertObject.ts similarity index 100% rename from src/errors/invertObject.ts rename to src/build/errors/invertObject.ts diff --git a/src/errors/transformErrorMessages.ts b/src/build/errors/transformErrorMessages.ts similarity index 99% rename from src/errors/transformErrorMessages.ts rename to src/build/errors/transformErrorMessages.ts index 08d144234..d89e6cbc7 100644 --- a/src/errors/transformErrorMessages.ts +++ b/src/build/errors/transformErrorMessages.ts @@ -1,8 +1,10 @@ import fs from 'fs'; +import { addDefault } from '@babel/helper-module-imports'; + +import { paths } from '../../constants'; + import { invertObject } from './invertObject'; import { evalToString } from './evalToString'; -import { addDefault } from '@babel/helper-module-imports'; -import { paths } from '../constants'; export default function transformErrorMessages(babel: any) { const t = babel.types; diff --git a/src/build/index.ts b/src/build/index.ts new file mode 100644 index 000000000..01d10153a --- /dev/null +++ b/src/build/index.ts @@ -0,0 +1,297 @@ +import * as fs from 'fs-extra'; +import path from 'path'; +import util from 'util'; +import chalk from 'chalk'; +import rimraf from 'rimraf'; +import asyncro from 'asyncro'; +import ora from 'ora'; +import execa from 'execa'; +import { + rollup, + watch, + RollupOptions, + OutputOptions, + RollupWatchOptions, + WatcherOptions, +} from 'rollup'; +import glob from 'tiny-glob/sync'; +import { concatAllArray } from 'jpjs'; + +import { paths } from '../constants'; +import { appPackageJson } from '../files'; +import logError from '../logError'; +import { resolveApp, safePackageName, clearConsole } from '../utils'; + +import { WatchOpts, BuildOpts, ModuleFormat, NormalizedOpts } from './types'; +import { createBuildConfigs } from './createBuildConfigs'; +import { createProgressEstimator } from './createProgressEstimator'; + +export function addBuildCommand(prog: any) { + prog + .command('build') + .describe('Build your project once and exit') + .option('--entry, -i', 'Entry module(s)') + .example('build --entry src/foo.tsx') + .option('--target', 'Specify your target environment', 'browser') + .example('build --target node') + .option('--name', 'Specify name exposed in UMD builds') + .example('build --name Foo') + .option('--format', 'Specify module format(s)', 'cjs,esm') + .example('build --format cjs,esm') + .option('--tsconfig', 'Specify custom tsconfig path') + .example('build --tsconfig ./tsconfig.foo.json') + .option('--transpileOnly', 'Skip type checking', false) + .example('build --transpileOnly') + .option( + '--extractErrors', + 'Extract errors to ./errors/codes.json and provide a url for decoding.' + ) + .example( + 'build --extractErrors=https://reactjs.org/docs/error-decoder.html?invariant=' + ) + .action(buildAction); +} + +export function addWatchCommand(prog: any) { + prog + .command('watch') + .describe('Rebuilds on any change') + .option('--entry, -i', 'Entry module(s)') + .example('watch --entry src/foo.tsx') + .option('--target', 'Specify your target environment', 'browser') + .example('watch --target node') + .option('--name', 'Specify name exposed in UMD builds') + .example('watch --name Foo') + .option('--format', 'Specify module format(s)', 'cjs,esm') + .example('watch --format cjs,esm') + .option( + '--verbose', + 'Keep outdated console output in watch mode instead of clearing the screen' + ) + .example('watch --verbose') + .option('--noClean', "Don't clean the dist folder") + .example('watch --noClean') + .option('--tsconfig', 'Specify custom tsconfig path') + .example('watch --tsconfig ./tsconfig.foo.json') + .example('build --tsconfig ./tsconfig.foo.json') + .option('--onFirstSuccess', 'Run a command on the first successful build') + .example('watch --onFirstSuccess "echo The first successful build!"') + .option('--onSuccess', 'Run a command on a successful build') + .example('watch --onSuccess "echo Successful build!"') + .option('--onFailure', 'Run a command on a failed build') + .example('watch --onFailure "The build failed!"') + .option('--transpileOnly', 'Skip type checking', false) + .example('build --transpileOnly') + .option( + '--extractErrors', + 'Extract invariant errors to ./errors/codes.json.' + ) + .example('build --extractErrors') + .action(watchAction); +} + +async function buildAction(dirtyOpts: BuildOpts) { + const opts = await normalizeOpts(dirtyOpts); + const buildConfigs = await createBuildConfigs(opts); + await cleanDistFolder(); + const logger = await createProgressEstimator(); + if (opts.format.includes('cjs')) { + const promise = writeCjsEntryFile(opts.name).catch(logError); + logger(promise, 'Creating entry file'); + } + try { + const promise = asyncro + .map( + buildConfigs, + async (inputOptions: RollupOptions & { output: OutputOptions }) => { + let bundle = await rollup(inputOptions); + await bundle.write(inputOptions.output); + await moveTypes(); + } + ) + .catch((e: any) => { + throw e; + }); + logger(promise, 'Building modules'); + await promise; + } catch (error) { + logError(error); + process.exit(1); + } +} + +async function watchAction(dirtyOpts: WatchOpts) { + const opts = await normalizeOpts(dirtyOpts); + const buildConfigs = await createBuildConfigs(opts); + if (!opts.noClean) { + await cleanDistFolder(); + } + opts.name = opts.name || appPackageJson.name; + opts.input = await getInputs(opts.entry, appPackageJson.source); + if (opts.format.includes('cjs')) { + await writeCjsEntryFile(opts.name); + } + + type Killer = execa.ExecaChildProcess | null; + + let firstTime = true; + let successKiller: Killer = null; + let failureKiller: Killer = null; + + function run(command?: string) { + if (!command) { + return null; + } + + const [exec, ...args] = command.split(' '); + return execa(exec, args, { + stdio: 'inherit', + }); + } + + function killHooks() { + return Promise.all([ + successKiller ? successKiller.kill('SIGTERM') : null, + failureKiller ? failureKiller.kill('SIGTERM') : null, + ]); + } + + const spinner = ora().start(); + await watch( + (buildConfigs as RollupWatchOptions[]).map(inputOptions => ({ + watch: { + silent: true, + include: ['src/**'], + exclude: ['node_modules/**'], + } as WatcherOptions, + ...inputOptions, + })) + ).on('event', async event => { + // clear previous onSuccess/onFailure hook processes so they don't pile up + await killHooks(); + + if (event.code === 'START') { + if (!opts.verbose) { + clearConsole(); + } + spinner.start(chalk.bold.cyan('Compiling modules...')); + } + if (event.code === 'ERROR') { + spinner.fail(chalk.bold.red('Failed to compile')); + logError(event.error); + failureKiller = run(opts.onFailure); + } + if (event.code === 'FATAL') { + spinner.fail(chalk.bold.red('Failed to compile')); + logError(event.error); + failureKiller = run(opts.onFailure); + } + if (event.code === 'END') { + spinner.succeed(chalk.bold.green('Compiled successfully')); + console.log(` +${chalk.dim('Watching for changes')} +`); + + try { + await moveTypes(); + + if (firstTime && opts.onFirstSuccess) { + firstTime = false; + run(opts.onFirstSuccess); + } else { + successKiller = run(opts.onSuccess); + } + } catch (_error) {} + } + }); +} + +async function normalizeOpts(opts: WatchOpts): Promise { + return { + ...opts, + name: opts.name || appPackageJson.name, + input: await getInputs(opts.entry, appPackageJson.source), + format: opts.format.split(',').map((format: string) => { + if (format === 'es') { + return 'esm'; + } + return format; + }) as [ModuleFormat, ...ModuleFormat[]], + }; +} + +async function getInputs( + entries?: string | string[], + source?: string +): Promise { + let inputs: string[] = []; + let stub: any[] = []; + stub + .concat( + entries && entries.length + ? entries + : (source && resolveApp(source)) || + ((await isDir(resolveApp('src'))) && (await jsOrTs('src/index'))) + ) + .map(file => glob(file)) + .forEach(input => inputs.push(input)); + + return concatAllArray(inputs); +} + +const isDir = (name: string) => + fs + .stat(name) + .then(stats => stats.isDirectory()) + .catch(() => false); + +const isFile = (name: string) => + fs + .stat(name) + .then(stats => stats.isFile()) + .catch(() => false); + +async function jsOrTs(filename: string) { + const extension = (await isFile(resolveApp(filename + '.ts'))) + ? '.ts' + : (await isFile(resolveApp(filename + '.tsx'))) + ? '.tsx' + : '.js'; + + return resolveApp(`${filename}${extension}`); +} + +async function moveTypes() { + try { + // Move the typescript types to the base of the ./dist folder + await fs.copy(paths.appDist + '/src', paths.appDist, { + overwrite: true, + }); + await fs.remove(paths.appDist + '/src'); + } catch (e) {} +} + +async function cleanDistFolder() { + try { + await util.promisify(fs.access)(paths.appDist); + return util.promisify(rimraf)(paths.appDist); + } catch { + // if an exception is throw, the files does not exists or it is not visible + // either way, we just return + return; + } +} + +function writeCjsEntryFile(name: string) { + const baseLine = `module.exports = require('./${safePackageName(name)}`; + const contents = ` +'use strict' + +if (process.env.NODE_ENV === 'production') { +${baseLine}.cjs.production.min.js') +} else { +${baseLine}.cjs.development.js') +} +`; + return fs.outputFile(path.join(paths.appDist, 'index.js'), contents); +} diff --git a/src/build/types.ts b/src/build/types.ts new file mode 100644 index 000000000..ad9d3bd2f --- /dev/null +++ b/src/build/types.ts @@ -0,0 +1,50 @@ +interface SharedOpts { + // JS target + target: 'node' | 'browser'; + // Path to tsconfig file + tsconfig?: string; + // Is error extraction running? + extractErrors?: boolean; +} + +export type ModuleFormat = 'cjs' | 'umd' | 'esm' | 'system'; + +export interface BuildOpts extends SharedOpts { + name?: string; + entry?: string | string[]; + format: 'cjs,esm'; + target: 'browser'; +} + +export interface WatchOpts extends BuildOpts { + verbose?: boolean; + noClean?: boolean; + // callback hooks + onFirstSuccess?: string; + onSuccess?: string; + onFailure?: string; +} + +export interface NormalizedOpts + extends Omit { + name: string; + input: string[]; + format: [ModuleFormat, ...ModuleFormat[]]; +} + +export interface TsdxOptions extends SharedOpts { + // Name of package + name: string; + // path to file + input: string; + // Environment + env: 'development' | 'production'; + // Module format + format: ModuleFormat; + // Is minifying? + minify?: boolean; + // Is this the very first rollup config (and thus should one-off metadata be extracted)? + writeMeta?: boolean; + // Only transpile, do not type check (makes compilation faster) + transpileOnly?: boolean; +} diff --git a/src/getInstallArgs.ts b/src/create/getInstallArgs.ts similarity index 100% rename from src/getInstallArgs.ts rename to src/create/getInstallArgs.ts diff --git a/src/getInstallCmd.ts b/src/create/getInstallCmd.ts similarity index 100% rename from src/getInstallCmd.ts rename to src/create/getInstallCmd.ts diff --git a/src/create/index.ts b/src/create/index.ts new file mode 100644 index 000000000..9da9c8106 --- /dev/null +++ b/src/create/index.ts @@ -0,0 +1,204 @@ +import * as fs from 'fs-extra'; +import path from 'path'; +import util from 'util'; +import chalk from 'chalk'; +import ora from 'ora'; +import execa from 'execa'; +import shell from 'shelljs'; +import { Input, Select } from 'enquirer'; + +import logError from '../logError'; +import { safePackageName } from '../utils'; + +import { templates } from './templates'; +import { composePackageJson } from './templates/utils'; +import getInstallCmd from './getInstallCmd'; +import getInstallArgs from './getInstallArgs'; +import * as Messages from './messages'; + +const pkg = require('../../package.json'); + +export function addCreateCommand(prog: any) { + prog + .version(pkg.version) + .command('create ') + .describe('Create a new package with TSDX') + .example('create mypackage') + .option( + '--template', + `Specify a template. Allowed choices: [${Object.keys(templates).join( + ', ' + )}]` + ) + .example('create --template react mypackage') + .action(createAction); +} + +async function createAction(pkg: string, opts: any) { + console.log( + chalk.blue(` +::::::::::: :::::::: ::::::::: ::: ::: + :+: :+: :+: :+: :+: :+: :+: + +:+ +:+ +:+ +:+ +:+ +:+ + +#+ +#++:++#++ +#+ +:+ +#++:+ + +#+ +#+ +#+ +#+ +#+ +#+ + #+# #+# #+# #+# #+# #+# #+# + ### ######## ######### ### ### +`) + ); + const bootSpinner = ora(`Creating ${chalk.bold.green(pkg)}...`); + let template; + // Helper fn to prompt the user for a different + // folder name if one already exists + async function getProjectPath(projectPath: string): Promise { + let exists = true; + try { + // will throw an exception if it does not exists + await util.promisify(fs.access)(projectPath); + } catch { + exists = false; + } + if (!exists) { + return projectPath; + } + bootSpinner.fail(`Failed to create ${chalk.bold.red(pkg)}`); + const prompt = new Input({ + message: `A folder named ${chalk.bold.red( + pkg + )} already exists! ${chalk.bold('Choose a different name')}`, + initial: pkg + '-1', + result: (v: string) => v.trim(), + }); + pkg = await prompt.run(); + projectPath = (await fs.realpath(process.cwd())) + '/' + pkg; + bootSpinner.start(`Creating ${chalk.bold.green(pkg)}...`); + return await getProjectPath(projectPath); // recursion! + } + + try { + // get the project path + const realPath = await fs.realpath(process.cwd()); + let projectPath = await getProjectPath(realPath + '/' + pkg); + + const prompt = new Select({ + message: 'Choose a template', + choices: Object.keys(templates), + }); + + if (opts.template) { + template = opts.template.trim(); + if (!prompt.choices.includes(template)) { + bootSpinner.fail(`Invalid template ${chalk.bold.red(template)}`); + template = await prompt.run(); + } + } else { + template = await prompt.run(); + } + + bootSpinner.start(); + // copy the template + await fs.copy( + path.resolve(__dirname, `../../templates/${template}`), + projectPath, + { + overwrite: true, + } + ); + // fix gitignore + await fs.move( + path.resolve(projectPath, './gitignore'), + path.resolve(projectPath, './.gitignore') + ); + + // update license year and author + let license: string = await fs.readFile( + path.resolve(projectPath, 'LICENSE'), + { encoding: 'utf-8' } + ); + + license = license.replace(//, `${new Date().getFullYear()}`); + + // attempt to automatically derive author name + let author = getAuthorName(); + + if (!author) { + bootSpinner.stop(); + const licenseInput = new Input({ + name: 'author', + message: 'Who is the package author?', + }); + author = await licenseInput.run(); + setAuthorName(author); + bootSpinner.start(); + } + + license = license.replace(//, author.trim()); + + await fs.writeFile(path.resolve(projectPath, 'LICENSE'), license, { + encoding: 'utf-8', + }); + + const templateConfig = templates[template as keyof typeof templates]; + const generatePackageJson = composePackageJson(templateConfig); + + // Install deps + process.chdir(projectPath); + const safeName = safePackageName(pkg); + const pkgJson = generatePackageJson({ name: safeName, author }); + await fs.outputJSON(path.resolve(projectPath, 'package.json'), pkgJson); + bootSpinner.succeed(`Created ${chalk.bold.green(pkg)}`); + await Messages.start(pkg); + } catch (error) { + bootSpinner.fail(`Failed to create ${chalk.bold.red(pkg)}`); + logError(error); + process.exit(1); + } + + const templateConfig = templates[template as keyof typeof templates]; + const { dependencies: deps } = templateConfig; + + const installSpinner = ora(Messages.installing(deps.sort())).start(); + try { + const cmd = await getInstallCmd(); + await execa(cmd, getInstallArgs(cmd, deps)); + installSpinner.succeed('Installed dependencies'); + console.log(await Messages.start(pkg)); + } catch (error) { + installSpinner.fail('Failed to install dependencies'); + logError(error); + process.exit(1); + } +} + +function getAuthorName() { + let author = ''; + + author = shell + .exec('npm config get init-author-name', { silent: true }) + .stdout.trim(); + if (author) return author; + + author = shell + .exec('git config --global user.name', { silent: true }) + .stdout.trim(); + if (author) { + setAuthorName(author); + return author; + } + + author = shell + .exec('npm config get init-author-email', { silent: true }) + .stdout.trim(); + if (author) return author; + + author = shell + .exec('git config --global user.email', { silent: true }) + .stdout.trim(); + if (author) return author; + + return author; +} + +function setAuthorName(author: string) { + shell.exec(`npm config set init-author-name "${author}"`, { silent: true }); +} diff --git a/src/messages.ts b/src/create/messages.ts similarity index 99% rename from src/messages.ts rename to src/create/messages.ts index 2f82cefe9..67912f1f1 100644 --- a/src/messages.ts +++ b/src/create/messages.ts @@ -1,4 +1,5 @@ import chalk from 'chalk'; + import getInstallCmd from './getInstallCmd'; import * as Output from './output'; @@ -72,19 +73,19 @@ export const start = async function(projectName: string) { return ` ${chalk.green('Awesome!')} You're now ready to start coding. - + I already ran ${Output.cmd(commands.install)} for you, so your next steps are: ${Output.cmd(`cd ${projectName}`)} - + To start developing (rebuilds on changes): ${Output.cmd(commands.start)} - + To build for production: ${Output.cmd(commands.build)} To test your library with Jest: ${Output.cmd(commands.test)} - + Questions? Feedback? Please let me know! ${chalk.green('https://github.com/jaredpalmer/tsdx/issues')} `; diff --git a/src/output.ts b/src/create/output.ts similarity index 100% rename from src/output.ts rename to src/create/output.ts diff --git a/src/templates/basic.ts b/src/create/templates/basic.ts similarity index 100% rename from src/templates/basic.ts rename to src/create/templates/basic.ts diff --git a/src/templates/index.ts b/src/create/templates/index.ts similarity index 100% rename from src/templates/index.ts rename to src/create/templates/index.ts diff --git a/src/templates/react-with-storybook.ts b/src/create/templates/react-with-storybook.ts similarity index 100% rename from src/templates/react-with-storybook.ts rename to src/create/templates/react-with-storybook.ts diff --git a/src/templates/react.ts b/src/create/templates/react.ts similarity index 100% rename from src/templates/react.ts rename to src/create/templates/react.ts diff --git a/src/templates/template.d.ts b/src/create/templates/template.d.ts similarity index 100% rename from src/templates/template.d.ts rename to src/create/templates/template.d.ts diff --git a/src/templates/utils/index.ts b/src/create/templates/utils/index.ts similarity index 100% rename from src/templates/utils/index.ts rename to src/create/templates/utils/index.ts diff --git a/src/files.ts b/src/files.ts new file mode 100644 index 000000000..45f230024 --- /dev/null +++ b/src/files.ts @@ -0,0 +1,12 @@ +import fs from 'fs-extra'; + +import { paths } from './constants'; +import { PackageJson } from './types'; + +export let appPackageJson: PackageJson; +function setAppPackageJson() { + try { + appPackageJson = fs.readJSONSync(paths.appPackageJson); + } catch (e) {} +} +setAppPackageJson(); diff --git a/src/index.ts b/src/index.ts index 2096a29c9..804b371c8 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1,605 +1,18 @@ #!/usr/bin/env node import sade from 'sade'; -import glob from 'tiny-glob/sync'; -import { - rollup, - watch, - RollupOptions, - OutputOptions, - RollupWatchOptions, - WatcherOptions, -} from 'rollup'; -import asyncro from 'asyncro'; -import chalk from 'chalk'; -import util from 'util'; -import * as fs from 'fs-extra'; -import jest from 'jest'; -import { CLIEngine } from 'eslint'; -import logError from './logError'; -import path from 'path'; -import rimraf from 'rimraf'; -import execa from 'execa'; -import shell from 'shelljs'; -import ora from 'ora'; -import { paths } from './constants'; -import * as Messages from './messages'; -import { createBuildConfigs } from './createBuildConfigs'; -import { createJestConfig } from './createJestConfig'; -import { createEslintConfig } from './createEslintConfig'; -import { resolveApp, safePackageName, clearConsole } from './utils'; -import { concatAllArray } from 'jpjs'; -import getInstallCmd from './getInstallCmd'; -import getInstallArgs from './getInstallArgs'; -import { Input, Select } from 'enquirer'; -import { - PackageJson, - WatchOpts, - BuildOpts, - ModuleFormat, - NormalizedOpts, -} from './types'; -import { createProgressEstimator } from './createProgressEstimator'; -import { templates } from './templates'; -import { composePackageJson } from './templates/utils'; -const pkg = require('../package.json'); -const prog = sade('tsdx'); - -let appPackageJson: PackageJson; - -try { - appPackageJson = fs.readJSONSync(paths.appPackageJson); -} catch (e) {} - -export const isDir = (name: string) => - fs - .stat(name) - .then(stats => stats.isDirectory()) - .catch(() => false); - -export const isFile = (name: string) => - fs - .stat(name) - .then(stats => stats.isFile()) - .catch(() => false); - -async function jsOrTs(filename: string) { - const extension = (await isFile(resolveApp(filename + '.ts'))) - ? '.ts' - : (await isFile(resolveApp(filename + '.tsx'))) - ? '.tsx' - : '.js'; - - return resolveApp(`${filename}${extension}`); -} - -async function getInputs( - entries?: string | string[], - source?: string -): Promise { - let inputs: string[] = []; - let stub: any[] = []; - stub - .concat( - entries && entries.length - ? entries - : (source && resolveApp(source)) || - ((await isDir(resolveApp('src'))) && (await jsOrTs('src/index'))) - ) - .map(file => glob(file)) - .forEach(input => inputs.push(input)); - - return concatAllArray(inputs); -} - -async function moveTypes() { - try { - // Move the typescript types to the base of the ./dist folder - await fs.copy(paths.appDist + '/src', paths.appDist, { - overwrite: true, - }); - await fs.remove(paths.appDist + '/src'); - } catch (e) {} -} - -prog - .version(pkg.version) - .command('create ') - .describe('Create a new package with TSDX') - .example('create mypackage') - .option( - '--template', - `Specify a template. Allowed choices: [${Object.keys(templates).join( - ', ' - )}]` - ) - .example('create --template react mypackage') - .action(async (pkg: string, opts: any) => { - console.log( - chalk.blue(` -::::::::::: :::::::: ::::::::: ::: ::: - :+: :+: :+: :+: :+: :+: :+: - +:+ +:+ +:+ +:+ +:+ +:+ - +#+ +#++:++#++ +#+ +:+ +#++:+ - +#+ +#+ +#+ +#+ +#+ +#+ - #+# #+# #+# #+# #+# #+# #+# - ### ######## ######### ### ### -`) - ); - const bootSpinner = ora(`Creating ${chalk.bold.green(pkg)}...`); - let template; - // Helper fn to prompt the user for a different - // folder name if one already exists - async function getProjectPath(projectPath: string): Promise { - let exists = true; - try { - // will throw an exception if it does not exists - await util.promisify(fs.access)(projectPath); - } catch { - exists = false; - } - if (!exists) { - return projectPath; - } - bootSpinner.fail(`Failed to create ${chalk.bold.red(pkg)}`); - const prompt = new Input({ - message: `A folder named ${chalk.bold.red( - pkg - )} already exists! ${chalk.bold('Choose a different name')}`, - initial: pkg + '-1', - result: (v: string) => v.trim(), - }); - pkg = await prompt.run(); - projectPath = (await fs.realpath(process.cwd())) + '/' + pkg; - bootSpinner.start(`Creating ${chalk.bold.green(pkg)}...`); - return await getProjectPath(projectPath); // recursion! - } - - try { - // get the project path - const realPath = await fs.realpath(process.cwd()); - let projectPath = await getProjectPath(realPath + '/' + pkg); - - const prompt = new Select({ - message: 'Choose a template', - choices: Object.keys(templates), - }); - - if (opts.template) { - template = opts.template.trim(); - if (!prompt.choices.includes(template)) { - bootSpinner.fail(`Invalid template ${chalk.bold.red(template)}`); - template = await prompt.run(); - } - } else { - template = await prompt.run(); - } - - bootSpinner.start(); - // copy the template - await fs.copy( - path.resolve(__dirname, `../templates/${template}`), - projectPath, - { - overwrite: true, - } - ); - // fix gitignore - await fs.move( - path.resolve(projectPath, './gitignore'), - path.resolve(projectPath, './.gitignore') - ); - - // update license year and author - let license: string = await fs.readFile( - path.resolve(projectPath, 'LICENSE'), - { encoding: 'utf-8' } - ); - - license = license.replace(//, `${new Date().getFullYear()}`); - - // attempt to automatically derive author name - let author = getAuthorName(); - - if (!author) { - bootSpinner.stop(); - const licenseInput = new Input({ - name: 'author', - message: 'Who is the package author?', - }); - author = await licenseInput.run(); - setAuthorName(author); - bootSpinner.start(); - } - - license = license.replace(//, author.trim()); - - await fs.writeFile(path.resolve(projectPath, 'LICENSE'), license, { - encoding: 'utf-8', - }); - - const templateConfig = templates[template as keyof typeof templates]; - const generatePackageJson = composePackageJson(templateConfig); - - // Install deps - process.chdir(projectPath); - const safeName = safePackageName(pkg); - const pkgJson = generatePackageJson({ name: safeName, author }); - await fs.outputJSON(path.resolve(projectPath, 'package.json'), pkgJson); - bootSpinner.succeed(`Created ${chalk.bold.green(pkg)}`); - await Messages.start(pkg); - } catch (error) { - bootSpinner.fail(`Failed to create ${chalk.bold.red(pkg)}`); - logError(error); - process.exit(1); - } - - const templateConfig = templates[template as keyof typeof templates]; - const { dependencies: deps } = templateConfig; - - const installSpinner = ora(Messages.installing(deps.sort())).start(); - try { - const cmd = await getInstallCmd(); - await execa(cmd, getInstallArgs(cmd, deps)); - installSpinner.succeed('Installed dependencies'); - console.log(await Messages.start(pkg)); - } catch (error) { - installSpinner.fail('Failed to install dependencies'); - logError(error); - process.exit(1); - } - }); - -prog - .command('watch') - .describe('Rebuilds on any change') - .option('--entry, -i', 'Entry module(s)') - .example('watch --entry src/foo.tsx') - .option('--target', 'Specify your target environment', 'browser') - .example('watch --target node') - .option('--name', 'Specify name exposed in UMD builds') - .example('watch --name Foo') - .option('--format', 'Specify module format(s)', 'cjs,esm') - .example('watch --format cjs,esm') - .option( - '--verbose', - 'Keep outdated console output in watch mode instead of clearing the screen' - ) - .example('watch --verbose') - .option('--noClean', "Don't clean the dist folder") - .example('watch --noClean') - .option('--tsconfig', 'Specify custom tsconfig path') - .example('watch --tsconfig ./tsconfig.foo.json') - .example('build --tsconfig ./tsconfig.foo.json') - .option('--onFirstSuccess', 'Run a command on the first successful build') - .example('watch --onFirstSuccess "echo The first successful build!"') - .option('--onSuccess', 'Run a command on a successful build') - .example('watch --onSuccess "echo Successful build!"') - .option('--onFailure', 'Run a command on a failed build') - .example('watch --onFailure "The build failed!"') - .option('--transpileOnly', 'Skip type checking', false) - .example('build --transpileOnly') - .option('--extractErrors', 'Extract invariant errors to ./errors/codes.json.') - .example('build --extractErrors') - .action(async (dirtyOpts: WatchOpts) => { - const opts = await normalizeOpts(dirtyOpts); - const buildConfigs = await createBuildConfigs(opts); - if (!opts.noClean) { - await cleanDistFolder(); - } - opts.name = opts.name || appPackageJson.name; - opts.input = await getInputs(opts.entry, appPackageJson.source); - if (opts.format.includes('cjs')) { - await writeCjsEntryFile(opts.name); - } - - type Killer = execa.ExecaChildProcess | null; - - let firstTime = true; - let successKiller: Killer = null; - let failureKiller: Killer = null; +import { addBuildCommand, addWatchCommand } from './build/index'; +import { addTestCommand } from './test/index'; +import { addLintCommand } from './lint/index'; +import { addCreateCommand } from './create/index'; - function run(command?: string) { - if (!command) { - return null; - } - - const [exec, ...args] = command.split(' '); - return execa(exec, args, { - stdio: 'inherit', - }); - } - - function killHooks() { - return Promise.all([ - successKiller ? successKiller.kill('SIGTERM') : null, - failureKiller ? failureKiller.kill('SIGTERM') : null, - ]); - } - - const spinner = ora().start(); - await watch( - (buildConfigs as RollupWatchOptions[]).map(inputOptions => ({ - watch: { - silent: true, - include: ['src/**'], - exclude: ['node_modules/**'], - } as WatcherOptions, - ...inputOptions, - })) - ).on('event', async event => { - // clear previous onSuccess/onFailure hook processes so they don't pile up - await killHooks(); - - if (event.code === 'START') { - if (!opts.verbose) { - clearConsole(); - } - spinner.start(chalk.bold.cyan('Compiling modules...')); - } - if (event.code === 'ERROR') { - spinner.fail(chalk.bold.red('Failed to compile')); - logError(event.error); - failureKiller = run(opts.onFailure); - } - if (event.code === 'FATAL') { - spinner.fail(chalk.bold.red('Failed to compile')); - logError(event.error); - failureKiller = run(opts.onFailure); - } - if (event.code === 'END') { - spinner.succeed(chalk.bold.green('Compiled successfully')); - console.log(` - ${chalk.dim('Watching for changes')} -`); - - try { - await moveTypes(); - - if (firstTime && opts.onFirstSuccess) { - firstTime = false; - run(opts.onFirstSuccess); - } else { - successKiller = run(opts.onSuccess); - } - } catch (_error) {} - } - }); - }); - -prog - .command('build') - .describe('Build your project once and exit') - .option('--entry, -i', 'Entry module(s)') - .example('build --entry src/foo.tsx') - .option('--target', 'Specify your target environment', 'browser') - .example('build --target node') - .option('--name', 'Specify name exposed in UMD builds') - .example('build --name Foo') - .option('--format', 'Specify module format(s)', 'cjs,esm') - .example('build --format cjs,esm') - .option('--tsconfig', 'Specify custom tsconfig path') - .example('build --tsconfig ./tsconfig.foo.json') - .option('--transpileOnly', 'Skip type checking', false) - .example('build --transpileOnly') - .option( - '--extractErrors', - 'Extract errors to ./errors/codes.json and provide a url for decoding.' - ) - .example( - 'build --extractErrors=https://reactjs.org/docs/error-decoder.html?invariant=' - ) - .action(async (dirtyOpts: BuildOpts) => { - const opts = await normalizeOpts(dirtyOpts); - const buildConfigs = await createBuildConfigs(opts); - await cleanDistFolder(); - const logger = await createProgressEstimator(); - if (opts.format.includes('cjs')) { - const promise = writeCjsEntryFile(opts.name).catch(logError); - logger(promise, 'Creating entry file'); - } - try { - const promise = asyncro - .map( - buildConfigs, - async (inputOptions: RollupOptions & { output: OutputOptions }) => { - let bundle = await rollup(inputOptions); - await bundle.write(inputOptions.output); - await moveTypes(); - } - ) - .catch((e: any) => { - throw e; - }); - logger(promise, 'Building modules'); - await promise; - } catch (error) { - logError(error); - process.exit(1); - } - }); - -async function normalizeOpts(opts: WatchOpts): Promise { - return { - ...opts, - name: opts.name || appPackageJson.name, - input: await getInputs(opts.entry, appPackageJson.source), - format: opts.format.split(',').map((format: string) => { - if (format === 'es') { - return 'esm'; - } - return format; - }) as [ModuleFormat, ...ModuleFormat[]], - }; -} - -async function cleanDistFolder() { - try { - await util.promisify(fs.access)(paths.appDist); - return util.promisify(rimraf)(paths.appDist); - } catch { - // if an exception is throw, the files does not exists or it is not visible - // either way, we just return - return; - } -} - -function writeCjsEntryFile(name: string) { - const baseLine = `module.exports = require('./${safePackageName(name)}`; - const contents = ` -'use strict' - -if (process.env.NODE_ENV === 'production') { - ${baseLine}.cjs.production.min.js') -} else { - ${baseLine}.cjs.development.js') -} -`; - return fs.outputFile(path.join(paths.appDist, 'index.js'), contents); -} - -function getAuthorName() { - let author = ''; - - author = shell - .exec('npm config get init-author-name', { silent: true }) - .stdout.trim(); - if (author) return author; - - author = shell - .exec('git config --global user.name', { silent: true }) - .stdout.trim(); - if (author) { - setAuthorName(author); - return author; - } - - author = shell - .exec('npm config get init-author-email', { silent: true }) - .stdout.trim(); - if (author) return author; - - author = shell - .exec('git config --global user.email', { silent: true }) - .stdout.trim(); - if (author) return author; - - return author; -} - -function setAuthorName(author: string) { - shell.exec(`npm config set init-author-name "${author}"`, { silent: true }); -} - -prog - .command('test') - .describe( - 'Run jest test runner in watch mode. Passes through all flags directly to Jest' - ) - .action(async () => { - // Do this as the first thing so that any code reading it knows the right env. - process.env.BABEL_ENV = 'test'; - process.env.NODE_ENV = 'test'; - // Makes the script crash on unhandled rejections instead of silently - // ignoring them. In the future, promise rejections that are not handled will - // terminate the Node.js process with a non-zero exit code. - process.on('unhandledRejection', err => { - throw err; - }); - - const argv = process.argv.slice(2); - let jestConfig = { - ...createJestConfig( - relativePath => path.resolve(__dirname, '..', relativePath), - paths.appRoot - ), - ...appPackageJson.jest, - }; - try { - // Allow overriding with jest.config - const jestConfigContents = require(paths.jestConfig); - jestConfig = { ...jestConfig, ...jestConfigContents }; - } catch {} - - argv.push( - '--config', - JSON.stringify({ - ...jestConfig, - }) - ); - - if (!process.env.CI) { - argv.push('--watch'); // run jest in watch mode unless in CI - } - - const [, ...argsToPassToJestCli] = argv; - jest.run(argsToPassToJestCli); - }); - -prog - .command('lint') - .describe('Run eslint with Prettier') - .example('lint src test') - .option('--fix', 'Fixes fixable errors and warnings') - .example('lint src test --fix') - .option('--ignore-pattern', 'Ignore a pattern') - .example('lint src test --ignore-pattern test/foobar.ts') - .option('--write-file', 'Write the config file locally') - .example('lint --write-file') - .option('--report-file', 'Write JSON report to file locally') - .example('lint --report-file eslint-report.json') - .action( - async (opts: { - fix: boolean; - 'ignore-pattern': string; - 'write-file': boolean; - 'report-file': string; - _: string[]; - }) => { - if (opts['_'].length === 0 && !opts['write-file']) { - const defaultInputs = ['src', 'test'].filter(fs.existsSync); - opts['_'] = defaultInputs; - console.log( - chalk.yellow( - `Defaulting to "tsdx lint ${defaultInputs.join(' ')}"`, - '\nYou can override this in the package.json scripts, like "lint": "tsdx lint src otherDir"' - ) - ); - } - - const config = await createEslintConfig({ - pkg: appPackageJson, - rootDir: paths.appRoot, - writeFile: opts['write-file'], - }); - - const cli = new CLIEngine({ - baseConfig: { - ...config, - ...appPackageJson.eslint, - }, - extensions: ['.ts', '.tsx'], - fix: opts.fix, - ignorePattern: opts['ignore-pattern'], - }); - const report = cli.executeOnFiles(opts['_']); - if (opts.fix) { - CLIEngine.outputFixes(report); - } - console.log(cli.getFormatter()(report.results)); - if (opts['report-file']) { - await fs.mkdirs(path.dirname(opts['report-file'])); +const prog = sade('tsdx'); - await fs.writeFile( - opts['report-file'], - cli.getFormatter('json')(report.results) - ); - } - if (report.errorCount) { - process.exit(1); - } - } - ); +addBuildCommand(prog); +addWatchCommand(prog); +addTestCommand(prog); +addLintCommand(prog); +addCreateCommand(prog); prog.parse(process.argv); diff --git a/src/createEslintConfig.ts b/src/lint/createEslintConfig.ts similarity index 93% rename from src/createEslintConfig.ts rename to src/lint/createEslintConfig.ts index 41fbb4136..487fd31e4 100644 --- a/src/createEslintConfig.ts +++ b/src/lint/createEslintConfig.ts @@ -2,8 +2,9 @@ import fs from 'fs'; import path from 'path'; import util from 'util'; import { CLIEngine } from 'eslint'; -import { PackageJson } from './types'; -import { getReactVersion } from './utils'; + +import { PackageJson } from '../types'; +import { getReactVersion } from '../utils'; interface CreateEslintConfigArgs { pkg: PackageJson; diff --git a/src/lint/index.ts b/src/lint/index.ts new file mode 100644 index 000000000..dba0609b1 --- /dev/null +++ b/src/lint/index.ts @@ -0,0 +1,76 @@ +import * as fs from 'fs-extra'; +import path from 'path'; +import chalk from 'chalk'; +import { CLIEngine } from 'eslint'; + +import { paths } from '../constants'; +import { appPackageJson } from '../files'; + +import { createEslintConfig } from './createEslintConfig'; + +export function addLintCommand(prog: any) { + prog + .command('lint') + .describe('Run eslint with Prettier') + .example('lint src test') + .option('--fix', 'Fixes fixable errors and warnings') + .example('lint src test --fix') + .option('--ignore-pattern', 'Ignore a pattern') + .example('lint src test --ignore-pattern test/foobar.ts') + .option('--write-file', 'Write the config file locally') + .example('lint --write-file') + .option('--report-file', 'Write JSON report to file locally') + .example('lint --report-file eslint-report.json') + .action(lintAction); +} + +async function lintAction(opts: { + fix: boolean; + 'ignore-pattern': string; + 'write-file': boolean; + 'report-file': string; + _: string[]; +}) { + if (opts['_'].length === 0 && !opts['write-file']) { + const defaultInputs = ['src', 'test'].filter(fs.existsSync); + opts['_'] = defaultInputs; + console.log( + chalk.yellow( + `Defaulting to "tsdx lint ${defaultInputs.join(' ')}"`, + '\nYou can override this in the package.json scripts, like "lint": "tsdx lint src otherDir"' + ) + ); + } + + const config = await createEslintConfig({ + pkg: appPackageJson, + rootDir: paths.appRoot, + writeFile: opts['write-file'], + }); + + const cli = new CLIEngine({ + baseConfig: { + ...config, + ...appPackageJson.eslint, + }, + extensions: ['.ts', '.tsx'], + fix: opts.fix, + ignorePattern: opts['ignore-pattern'], + }); + const report = cli.executeOnFiles(opts['_']); + if (opts.fix) { + CLIEngine.outputFixes(report); + } + console.log(cli.getFormatter()(report.results)); + if (opts['report-file']) { + await fs.mkdirs(path.dirname(opts['report-file'])); + + await fs.writeFile( + opts['report-file'], + cli.getFormatter('json')(report.results) + ); + } + if (report.errorCount) { + process.exit(1); + } +} diff --git a/src/createJestConfig.ts b/src/test/createJestConfig.ts similarity index 100% rename from src/createJestConfig.ts rename to src/test/createJestConfig.ts diff --git a/src/test/index.ts b/src/test/index.ts new file mode 100644 index 000000000..732380665 --- /dev/null +++ b/src/test/index.ts @@ -0,0 +1,56 @@ +import path from 'path'; +import jest from 'jest'; + +import { paths } from '../constants'; +import { appPackageJson } from '../files'; + +import { createJestConfig } from './createJestConfig'; + +export function addTestCommand(prog: any) { + prog + .command('test') + .describe( + 'Run jest test runner in watch mode. Passes through all flags directly to Jest' + ) + .action(testAction); +} + +async function testAction() { + // Do this as the first thing so that any code reading it knows the right env. + process.env.BABEL_ENV = 'test'; + process.env.NODE_ENV = 'test'; + // Makes the script crash on unhandled rejections instead of silently + // ignoring them. In the future, promise rejections that are not handled will + // terminate the Node.js process with a non-zero exit code. + process.on('unhandledRejection', err => { + throw err; + }); + + const argv = process.argv.slice(2); + let jestConfig = { + ...createJestConfig( + relativePath => path.resolve(__dirname, '..', relativePath), + paths.appRoot + ), + ...appPackageJson.jest, + }; + try { + // Allow overriding with jest.config + const jestConfigContents = require(paths.jestConfig); + jestConfig = { ...jestConfig, ...jestConfigContents }; + } catch {} + + argv.push( + '--config', + JSON.stringify({ + ...jestConfig, + }) + ); + + if (!process.env.CI) { + argv.push('--watch'); // run jest in watch mode unless in CI + } + + const [, ...argsToPassToJestCli] = argv; + jest.run(argsToPassToJestCli); +} diff --git a/src/types.ts b/src/types.ts index e5e92cc5f..5a075c6b6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,54 +1,3 @@ -interface SharedOpts { - // JS target - target: 'node' | 'browser'; - // Path to tsconfig file - tsconfig?: string; - // Is error extraction running? - extractErrors?: boolean; -} - -export type ModuleFormat = 'cjs' | 'umd' | 'esm' | 'system'; - -export interface BuildOpts extends SharedOpts { - name?: string; - entry?: string | string[]; - format: 'cjs,esm'; - target: 'browser'; -} - -export interface WatchOpts extends BuildOpts { - verbose?: boolean; - noClean?: boolean; - // callback hooks - onFirstSuccess?: string; - onSuccess?: string; - onFailure?: string; -} - -export interface NormalizedOpts - extends Omit { - name: string; - input: string[]; - format: [ModuleFormat, ...ModuleFormat[]]; -} - -export interface TsdxOptions extends SharedOpts { - // Name of package - name: string; - // path to file - input: string; - // Environment - env: 'development' | 'production'; - // Module format - format: ModuleFormat; - // Is minifying? - minify?: boolean; - // Is this the very first rollup config (and thus should one-off metadata be extracted)? - writeMeta?: boolean; - // Only transpile, do not type check (makes compilation faster) - transpileOnly?: boolean; -} - export interface PackageJson { name: string; source?: string; diff --git a/src/utils.ts b/src/utils.ts index a7948ed23..6062ca324 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,7 @@ import fs from 'fs-extra'; import path from 'path'; import camelCase from 'camelcase'; + import { PackageJson } from './types'; // Remove the package name scope if it exists