diff --git a/src/createEslintConfig.ts b/src/createEslintConfig.ts index 46b656223..41fbb4136 100644 --- a/src/createEslintConfig.ts +++ b/src/createEslintConfig.ts @@ -1,5 +1,6 @@ import fs from 'fs'; import path from 'path'; +import util from 'util'; import { CLIEngine } from 'eslint'; import { PackageJson } from './types'; import { getReactVersion } from './utils'; @@ -9,11 +10,11 @@ interface CreateEslintConfigArgs { rootDir: string; writeFile: boolean; } -export function createEslintConfig({ +export async function createEslintConfig({ pkg, rootDir, writeFile, -}: CreateEslintConfigArgs): CLIEngine.Options['baseConfig'] { +}: CreateEslintConfigArgs): Promise { const isReactLibrary = Boolean(getReactVersion(pkg)); const config = { @@ -30,24 +31,27 @@ export function createEslintConfig({ }, }; - if (writeFile) { - const file = path.join(rootDir, '.eslintrc.js'); - if (fs.existsSync(file)) { + if (!writeFile) { + return config; + } + + const file = path.join(rootDir, '.eslintrc.js'); + try { + await util.promisify(fs.writeFile)( + file, + `module.exports = ${JSON.stringify(config, null, 2)}`, + { flag: 'wx' } + ); + } catch (e) { + if (e.code === 'EEXIST') { console.error( 'Error trying to save the Eslint configuration file:', `${file} already exists.` ); } else { - try { - fs.writeFileSync( - file, - `module.exports = ${JSON.stringify(config, null, 2)}` - ); - } catch (e) { - console.error(e); - } + console.error(e); } - } - return config; + return config; + } } diff --git a/src/createRollupConfig.ts b/src/createRollupConfig.ts index dd327f642..cd89bd082 100644 --- a/src/createRollupConfig.ts +++ b/src/createRollupConfig.ts @@ -20,8 +20,8 @@ const errorCodeOpts = { // shebang cache map thing because the transform only gets run once let shebang: any = {}; -export function createRollupConfig(opts: TsdxOptions) { - const findAndRecordErrorCodes = extractErrors({ +export async function createRollupConfig(opts: TsdxOptions) { + const findAndRecordErrorCodes = await extractErrors({ ...errorCodeOpts, ...opts, }); @@ -88,8 +88,8 @@ export function createRollupConfig(opts: TsdxOptions) { }, plugins: [ !!opts.extractErrors && { - transform(source: any) { - findAndRecordErrorCodes(source); + async transform(source: any) { + await findAndRecordErrorCodes(source); return source; }, }, diff --git a/src/errors/extractErrors.ts b/src/errors/extractErrors.ts index 878a2b74c..5236e043d 100644 --- a/src/errors/extractErrors.ts +++ b/src/errors/extractErrors.ts @@ -28,7 +28,7 @@ const babylonOptions = { ], }; -export function extractErrors(opts: any) { +export async function extractErrors(opts: any) { if (!opts || !('errorMapFilePath' in opts)) { throw new Error( 'Missing options. Ensure you pass an object with `errorMapFilePath`.' @@ -45,7 +45,7 @@ export function extractErrors(opts: any) { // Using `fs.readFileSync` instead of `require` here, because `require()` // calls are cached, and the cache map is not properly invalidated after // file changes. - existingErrorMap = JSON.parse(fs.readFileSync(errorMapFilePath, 'utf8')); + existingErrorMap = JSON.parse(await fs.readFile(errorMapFilePath, 'utf8')); } catch (e) { existingErrorMap = {}; } @@ -89,20 +89,20 @@ export function extractErrors(opts: any) { existingErrorMap[errorMsgLiteral] = '' + currentID++; } - function flush(cb?: any) { + async function flush() { const prettyName = pascalCase(safeVariableName(opts.name)); // Ensure that the ./src/errors directory exists or create it - fs.ensureDirSync(paths.appErrors); + await fs.ensureDir(paths.appErrors); // Output messages to ./errors/codes.json - fs.writeFileSync( + await fs.writeFile( errorMapFilePath, JSON.stringify(invertObject(existingErrorMap), null, 2) + '\n', 'utf-8' ); // Write the error files, unless they already exist - fs.writeFileSync( + await fs.writeFile( paths.appErrors + '/ErrorDev.js', ` function ErrorDev(message) { @@ -116,7 +116,7 @@ export default ErrorDev; 'utf-8' ); - fs.writeFileSync( + await fs.writeFile( paths.appErrors + '/ErrorProd.js', ` function ErrorProd(code) { @@ -138,8 +138,8 @@ export default ErrorProd; ); } - return function extractErrors(source: any) { + return async function extractErrors(source: any) { transform(source); - flush(); + await flush(); }; } diff --git a/src/getInstallCmd.ts b/src/getInstallCmd.ts index 0e74f7013..093f8f28a 100644 --- a/src/getInstallCmd.ts +++ b/src/getInstallCmd.ts @@ -4,13 +4,13 @@ let cmd: InstallCommand; export type InstallCommand = 'yarn' | 'npm'; -export default function getInstallCmd(): InstallCommand { +export default async function getInstallCmd(): Promise { if (cmd) { return cmd; } try { - execa.sync('yarnpkg', ['--version']); + await execa('yarnpkg', ['--version']); cmd = 'yarn'; } catch (e) { cmd = 'npm'; diff --git a/src/index.ts b/src/index.ts index 6aa87c0fe..ac1122c0f 100755 --- a/src/index.ts +++ b/src/index.ts @@ -94,61 +94,64 @@ async function getInputs(entries: string[], source?: string) { return concatAllArray(inputs); } -function createBuildConfigs( +async function createBuildConfigs( opts: any -): Array { - return concatAllArray( - opts.input.map((input: string) => - [ - opts.format.includes('cjs') && { - ...opts, - format: 'cjs', - env: 'development', - input, - }, - opts.format.includes('cjs') && { - ...opts, - format: 'cjs', - env: 'production', - input, - }, - opts.format.includes('esm') && { ...opts, format: 'esm', input }, - opts.format.includes('umd') && { - ...opts, - format: 'umd', - env: 'development', - input, - }, - opts.format.includes('umd') && { - ...opts, - format: 'umd', - env: 'production', - input, - }, - opts.format.includes('system') && { - ...opts, - format: 'system', - env: 'development', - input, - }, - opts.format.includes('system') && { - ...opts, - format: 'system', - env: 'production', - input, - }, - ] - .filter(Boolean) - .map((options: TsdxOptions, index: number) => ({ - ...options, - // We want to know if this is the first run for each entryfile - // for certain plugins (e.g. css) - writeMeta: index === 0, - })) - ) - ).map((options: TsdxOptions) => - // pass the full rollup config to tsdx.config.js override - tsdxConfig.rollup(createRollupConfig(options), options) +): Promise> { + return await Promise.all( + concatAllArray( + opts.input.map((input: string) => + [ + opts.format.includes('cjs') && { + ...opts, + format: 'cjs', + env: 'development', + input, + }, + opts.format.includes('cjs') && { + ...opts, + format: 'cjs', + env: 'production', + input, + }, + opts.format.includes('esm') && { ...opts, format: 'esm', input }, + opts.format.includes('umd') && { + ...opts, + format: 'umd', + env: 'development', + input, + }, + opts.format.includes('umd') && { + ...opts, + format: 'umd', + env: 'production', + input, + }, + opts.format.includes('system') && { + ...opts, + format: 'system', + env: 'development', + input, + }, + opts.format.includes('system') && { + ...opts, + format: 'system', + env: 'production', + input, + }, + ] + .filter(Boolean) + .map((options: TsdxOptions, index: number) => ({ + ...options, + // We want to know if this is the first run for each entryfile + // for certain plugins (e.g. css) + writeMeta: index === 0, + })) + ) + ).map(async (options: TsdxOptions) => { + // pass the full rollup config to tsdx.config.js override + const config = await createRollupConfig(options); + return tsdxConfig.rollup(config, options); + }) ); } @@ -186,29 +189,34 @@ prog // Helper fn to prompt the user for a different // folder name if one already exists async function getProjectPath(projectPath: string): Promise { - if (fs.existsSync(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 = fs.realpathSync(process.cwd()) + '/' + pkg; - bootSpinner.start(`Creating ${chalk.bold.green(pkg)}...`); - return getProjectPath(projectPath); // recursion! - } else { + 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 - let projectPath = await getProjectPath( - fs.realpathSync(process.cwd()) + '/' + pkg - ); + const realPath = await fs.realpath(process.cwd()); + let projectPath = await getProjectPath(realPath + '/' + pkg); const prompt = new Select({ message: 'Choose a template', @@ -241,9 +249,9 @@ prog ); // update license year and author - let license = fs.readFileSync( + let license: string = await fs.readFile( path.resolve(projectPath, 'LICENSE'), - 'utf-8' + { encoding: 'utf-8' } ); license = license.replace(//, `${new Date().getFullYear()}`); @@ -264,7 +272,7 @@ prog license = license.replace(//, author.trim()); - fs.writeFileSync(path.resolve(projectPath, 'LICENSE'), license, { + await fs.writeFile(path.resolve(projectPath, 'LICENSE'), license, { encoding: 'utf-8', }); @@ -304,7 +312,7 @@ prog }; await fs.outputJSON(path.resolve(projectPath, 'package.json'), pkgJson); bootSpinner.succeed(`Created ${chalk.bold.green(pkg)}`); - Messages.start(pkg); + await Messages.start(pkg); } catch (error) { bootSpinner.fail(`Failed to create ${chalk.bold.red(pkg)}`); logError(error); @@ -325,10 +333,10 @@ prog const installSpinner = ora(Messages.installing(deps)).start(); try { - const cmd = getInstallCmd(); + const cmd = await getInstallCmd(); await execa(cmd, getInstallArgs(cmd, deps)); installSpinner.succeed('Installed dependencies'); - console.log(Messages.start(pkg)); + console.log(await Messages.start(pkg)); } catch (error) { installSpinner.fail('Failed to install dependencies'); logError(error); @@ -360,7 +368,7 @@ prog .example('build --extractErrors') .action(async (dirtyOpts: any) => { const opts = await normalizeOpts(dirtyOpts); - const buildConfigs = createBuildConfigs(opts); + const buildConfigs = await createBuildConfigs(opts); if (!opts.noClean) { await cleanDistFolder(); } @@ -427,7 +435,7 @@ prog ) .action(async (dirtyOpts: any) => { const opts = await normalizeOpts(dirtyOpts); - const buildConfigs = createBuildConfigs(opts); + const buildConfigs = await createBuildConfigs(opts); await cleanDistFolder(); await ensureDistFolder(); const logger = await createProgressEstimator(); @@ -474,9 +482,14 @@ function ensureDistFolder() { return util.promisify(mkdirp)(paths.appDist); } -function cleanDistFolder() { - if (fs.existsSync(paths.appDist)) { +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; } } @@ -581,7 +594,7 @@ prog .option('--report-file', 'Write JSON report to file locally') .example('lint --report-file eslint-report.json') .action( - (opts: { + async (opts: { fix: boolean; 'ignore-pattern': string; 'write-file': boolean; @@ -599,13 +612,15 @@ prog ); } + const config = await createEslintConfig({ + pkg: appPackageJson, + rootDir: paths.appRoot, + writeFile: opts['write-file'], + }); + const cli = new CLIEngine({ baseConfig: { - ...createEslintConfig({ - pkg: appPackageJson, - rootDir: paths.appRoot, - writeFile: opts['write-file'], - }), + ...config, ...appPackageJson.eslint, }, extensions: ['.ts', '.tsx'], @@ -618,9 +633,9 @@ prog } console.log(cli.getFormatter()(report.results)); if (opts['report-file']) { - fs.mkdirsSync(path.dirname(opts['report-file'])); + await fs.mkdirs(path.dirname(opts['report-file'])); - fs.writeFileSync( + await fs.writeFile( opts['report-file'], cli.getFormatter('json')(report.results) ); diff --git a/src/messages.ts b/src/messages.ts index 5a949a96e..2f82cefe9 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -60,8 +60,8 @@ Creating ${chalk.bold(chalk.green(projectName))}... `; }; -export const start = function(projectName: string) { - const cmd = getInstallCmd(); +export const start = async function(projectName: string) { + const cmd = await getInstallCmd(); const commands = { install: cmd === 'npm' ? 'npm install' : 'yarn install',