- This is a demo content generated from
GrapesJS CLI .
- For the development, you should create a _index.html template file (might be a copy of this one) and on the next server start
- the new file will be served, and it will be ignored by git.
+
+
+ This is a demo content generated from GrapesJS CLI . For the development, you should create a _index.html
+ template file (might be a copy of this one) and on the next server start the new file will be served, and it
+ will be ignored by git.
-
diff --git a/packages/cli/src/build.ts b/packages/cli/src/build.ts
index 629f6831f..2afb77f0b 100644
--- a/packages/cli/src/build.ts
+++ b/packages/cli/src/build.ts
@@ -1,29 +1,29 @@
import {
- printRow,
- printError,
- buildWebpackArgs,
- normalizeJsonOpt,
- copyRecursiveSync,
- rootResolve,
- babelConfig,
- log,
- writeFile,
+ printRow,
+ printError,
+ buildWebpackArgs,
+ normalizeJsonOpt,
+ copyRecursiveSync,
+ rootResolve,
+ babelConfig,
+ log,
+ writeFile,
} from './utils';
-import { generateDtsBundle } from "dts-bundle-generator";
+import { generateDtsBundle } from 'dts-bundle-generator';
import webpack from 'webpack';
import fs from 'fs';
import webpackConfig from './webpack.config';
import { exec } from 'child_process';
import chalk from 'chalk';
-import rimraf from 'rimraf'
+import rimraf from 'rimraf';
import { transformFileSync } from '@babel/core';
interface BuildOptions {
- verbose?: boolean;
- patch?: boolean;
- statsOutput?: string;
- localePath?: string;
- dts?: 'include' | 'skip' | 'only';
+ verbose?: boolean;
+ patch?: boolean;
+ statsOutput?: string;
+ localePath?: string;
+ dts?: 'include' | 'skip' | 'only';
}
/**
@@ -31,114 +31,113 @@ interface BuildOptions {
* @param {Object} opts
*/
export const buildLocale = async (opts: BuildOptions = {}) => {
- const { localePath } = opts;
- if (!fs.existsSync(rootResolve(localePath))) return;
- printRow('Start building locale files...', { lineDown: 0 });
-
- await rimraf('locale');
-
- const localDst = rootResolve('locale');
- copyRecursiveSync(rootResolve(localePath), localDst);
-
- // Create locale/index.js file
- let result = '';
- fs.readdirSync(localDst).forEach(file => {
- const name = file.split('.')[0];
- result += `export { default as ${name} } from './${name}'\n`;
- });
- fs.writeFileSync(`${localDst}/index.js`, result);
-
- // Compile files
- const babelOpts = { ...babelConfig(buildWebpackArgs(opts) as any) };
- fs.readdirSync(localDst).forEach(file => {
- const filePath = `${localDst}/${file}`;
- const compiled = transformFileSync(filePath, babelOpts).code;
- fs.writeFileSync(filePath, compiled);
- });
-
- printRow('Locale files building completed successfully!');
-}
+ const { localePath } = opts;
+ if (!fs.existsSync(rootResolve(localePath))) return;
+ printRow('Start building locale files...', { lineDown: 0 });
+
+ await rimraf('locale');
+
+ const localDst = rootResolve('locale');
+ copyRecursiveSync(rootResolve(localePath), localDst);
+
+ // Create locale/index.js file
+ let result = '';
+ fs.readdirSync(localDst).forEach((file) => {
+ const name = file.split('.')[0];
+ result += `export { default as ${name} } from './${name}'\n`;
+ });
+ fs.writeFileSync(`${localDst}/index.js`, result);
+
+ // Compile files
+ const babelOpts = { ...babelConfig(buildWebpackArgs(opts) as any) };
+ fs.readdirSync(localDst).forEach((file) => {
+ const filePath = `${localDst}/${file}`;
+ const compiled = transformFileSync(filePath, babelOpts).code;
+ fs.writeFileSync(filePath, compiled);
+ });
+
+ printRow('Locale files building completed successfully!');
+};
/**
* Build TS declaration file
* @param {Object} opts
*/
- export const buildDeclaration = async (opts: BuildOptions = {}) => {
- const filePath = rootResolve('src/index.ts');
- if (!fs.existsSync(filePath)) return;
+export const buildDeclaration = async (opts: BuildOptions = {}) => {
+ const filePath = rootResolve('src/index.ts');
+ if (!fs.existsSync(filePath)) return;
- printRow('Start building TS declaration file...', { lineDown: 0 });
+ printRow('Start building TS declaration file...', { lineDown: 0 });
- const entry = { filePath, output: { noBanner: true }};
- const bundleOptions = { preferredConfigPath: rootResolve('tsconfig.json') };
- const result = generateDtsBundle([entry], bundleOptions)[0];
- await writeFile(rootResolve('dist/index.d.ts'), result);
+ const entry = { filePath, output: { noBanner: true } };
+ const bundleOptions = { preferredConfigPath: rootResolve('tsconfig.json') };
+ const result = generateDtsBundle([entry], bundleOptions)[0];
+ await writeFile(rootResolve('dist/index.d.ts'), result);
- printRow('TS declaration file building completed successfully!');
- }
+ printRow('TS declaration file building completed successfully!');
+};
/**
* Build the library files
* @param {Object} opts
*/
export default (opts: BuildOptions = {}) => {
- printRow('Start building the library...');
- const isVerb = opts.verbose;
- const { dts } = opts;
- isVerb && log(chalk.yellow('Build config:\n'), opts, '\n');
-
- const buildWebpack = () => {
- const buildConf = {
- ...webpackConfig({
- production: 1,
- args: buildWebpackArgs(opts),
- cmdOpts: opts,
- }),
- ...normalizeJsonOpt(opts, 'config'),
- };
-
- if (dts === 'only') {
- return buildDeclaration(opts);
- }
-
- webpack(buildConf, async (err, stats) => {
- const errors = err || (stats ? stats.hasErrors() : false);
- const statConf = {
- hash: false,
- colors: true,
- builtAt: false,
- entrypoints: false,
- modules: false,
- ...normalizeJsonOpt(opts, 'stats'),
- };
-
- if (stats) {
- opts.statsOutput &&
- fs.writeFileSync(rootResolve(opts.statsOutput), JSON.stringify(stats.toJson()));
- isVerb && log(chalk.yellow('Stats config:\n'), statConf, '\n');
- const result = stats.toString(statConf);
- log(result, '\n');
- }
-
- await buildLocale(opts);
-
- if (dts !== 'skip') {
- await buildDeclaration(opts);
- }
-
- if (errors) {
- printError(`Error during building`);
- console.error(err);
- } else {
- printRow('Building completed successfully!');
- }
- });
+ printRow('Start building the library...');
+ const isVerb = opts.verbose;
+ const { dts } = opts;
+ isVerb && log(chalk.yellow('Build config:\n'), opts, '\n');
+
+ const buildWebpack = () => {
+ const buildConf = {
+ ...webpackConfig({
+ production: 1,
+ args: buildWebpackArgs(opts),
+ cmdOpts: opts,
+ }),
+ ...normalizeJsonOpt(opts, 'config'),
};
- if (opts.patch) {
- isVerb && log(chalk.yellow('Patch the version'), '\n');
- exec('npm version --no-git-tag-version patch', buildWebpack);
- } else {
- buildWebpack();
+ if (dts === 'only') {
+ return buildDeclaration(opts);
}
+
+ webpack(buildConf, async (err, stats) => {
+ const errors = err || (stats ? stats.hasErrors() : false);
+ const statConf = {
+ hash: false,
+ colors: true,
+ builtAt: false,
+ entrypoints: false,
+ modules: false,
+ ...normalizeJsonOpt(opts, 'stats'),
+ };
+
+ if (stats) {
+ opts.statsOutput && fs.writeFileSync(rootResolve(opts.statsOutput), JSON.stringify(stats.toJson()));
+ isVerb && log(chalk.yellow('Stats config:\n'), statConf, '\n');
+ const result = stats.toString(statConf);
+ log(result, '\n');
+ }
+
+ await buildLocale(opts);
+
+ if (dts !== 'skip') {
+ await buildDeclaration(opts);
+ }
+
+ if (errors) {
+ printError(`Error during building`);
+ console.error(err);
+ } else {
+ printRow('Building completed successfully!');
+ }
+ });
+ };
+
+ if (opts.patch) {
+ isVerb && log(chalk.yellow('Patch the version'), '\n');
+ exec('npm version --no-git-tag-version patch', buildWebpack);
+ } else {
+ buildWebpack();
+ }
};
diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts
index fec96b58c..20ecd1803 100644
--- a/packages/cli/src/cli.ts
+++ b/packages/cli/src/cli.ts
@@ -6,158 +6,169 @@ import chalk from 'chalk';
import { printError } from './utils';
import { version } from '../package.json';
-yargs.usage(
- chalk.green.bold(
- fs.readFileSync(path.resolve(__dirname, './banner.txt'), 'utf8') +
- `\nv${version}`
- )
-);
+yargs.usage(chalk.green.bold(fs.readFileSync(path.resolve(__dirname, './banner.txt'), 'utf8') + `\nv${version}`));
-const webpackOptions = yargs => {
- yargs.positional('config', {
- describe: 'webpack configuration options',
- type: 'string',
- default: '{}',
+const webpackOptions = (yargs) => {
+ yargs
+ .positional('config', {
+ describe: 'webpack configuration options',
+ type: 'string',
+ default: '{}',
})
.positional('babel', {
- describe: 'Babel configuration object',
- type: 'string',
- default: '{}',
+ describe: 'Babel configuration object',
+ type: 'string',
+ default: '{}',
})
.positional('targets', {
- describe: 'Browser targets in browserslist query',
- type: 'string',
- default: '> 0.25%, not dead',
+ describe: 'Browser targets in browserslist query',
+ type: 'string',
+ default: '> 0.25%, not dead',
})
.positional('entry', {
- describe: 'Library entry point',
- type: 'string',
- default: 'src/index',
+ describe: 'Library entry point',
+ type: 'string',
+ default: 'src/index',
})
.positional('output', {
- describe: 'Build destination directory',
- type: 'string',
- default: 'dist',
- })
-}
+ describe: 'Build destination directory',
+ type: 'string',
+ default: 'dist',
+ });
+};
export const createCommands = (yargs) => {
- return yargs
- .command(['serve [port]', 'server'], 'Start the server', (yargs) => {
+ return yargs
+ .command(
+ ['serve [port]', 'server'],
+ 'Start the server',
+ (yargs) => {
yargs
- .positional('devServer', {
+ .positional('devServer', {
describe: 'webpack-dev-server options',
type: 'string',
default: '{}',
- })
- .positional('host', {
+ })
+ .positional('host', {
alias: 'h',
describe: 'Host to bind on',
type: 'string',
default: 'localhost',
- })
- .positional('port', {
+ })
+ .positional('port', {
alias: 'p',
describe: 'Port to bind on',
type: 'number',
default: 8080,
- })
- .positional('htmlWebpack', {
+ })
+ .positional('htmlWebpack', {
describe: 'html-webpack-plugin options',
type: 'string',
default: '{}',
- })
+ });
webpackOptions(yargs);
- }, (argv) => serve(argv))
- .command('build', 'Build the source', (yargs) => {
+ },
+ (argv) => serve(argv),
+ )
+ .command(
+ 'build',
+ 'Build the source',
+ (yargs) => {
yargs
- .positional('stats', {
+ .positional('stats', {
describe: 'Options for webpack Stats instance',
type: 'string',
default: '{}',
- })
- .positional('statsOutput', {
+ })
+ .positional('statsOutput', {
describe: 'Specify the path where to output webpack stats file (eg. "stats.json")',
type: 'string',
default: '',
- })
- .positional('patch', {
+ })
+ .positional('patch', {
describe: 'Increase automatically the patch version',
type: 'boolean',
default: true,
- })
- .positional('localePath', {
+ })
+ .positional('localePath', {
describe: 'Path to the directory containing locale files',
type: 'string',
default: 'src/locale',
- })
- .positional('dts', {
+ })
+ .positional('dts', {
describe: 'Generate typescript dts file ("include", "skip", "only")',
type: 'string',
default: 'include',
- });
+ });
webpackOptions(yargs);
- }, (argv) => build(argv))
- .command('init', 'Init GrapesJS plugin project', (yargs) => {
+ },
+ (argv) => build(argv),
+ )
+ .command(
+ 'init',
+ 'Init GrapesJS plugin project',
+ (yargs) => {
yargs
- .positional('yes', {
+ .positional('yes', {
alias: 'y',
describe: 'All default answers',
type: 'boolean',
default: false,
- })
- .positional('name', {
+ })
+ .positional('name', {
describe: 'Name of the project',
type: 'string',
- })
- .positional('rName', {
+ })
+ .positional('rName', {
describe: 'Repository name',
type: 'string',
- })
- .positional('user', {
+ })
+ .positional('user', {
describe: 'Repository username',
type: 'string',
- })
- .positional('components', {
+ })
+ .positional('components', {
describe: 'Indicate to include custom component types API',
type: 'boolean',
- })
- .positional('blocks', {
+ })
+ .positional('blocks', {
describe: 'Indicate to include blocks API',
type: 'boolean',
- })
- .positional('i18n', {
+ })
+ .positional('i18n', {
describe: 'Indicate to include the support for i18n',
type: 'boolean',
- })
- .positional('license', {
+ })
+ .positional('license', {
describe: 'License of the project',
type: 'string',
- })
- }, (argv) => init(argv))
+ });
+ },
+ (argv) => init(argv),
+ )
.options({
- verbose: {
- alias: 'v',
- description: 'Run with verbose logging',
- type: 'boolean', // boolean | number | string
- default: false,
- },
+ verbose: {
+ alias: 'v',
+ description: 'Run with verbose logging',
+ type: 'boolean', // boolean | number | string
+ default: false,
+ },
})
.recommendCommands()
- .strict()
-}
+ .strict();
+};
export const argsToOpts = async () => {
- return await createCommands(yargs).parse();
+ return await createCommands(yargs).parse();
};
export const run = async (opts = {}) => {
- try {
- let options = await argsToOpts();
- if (!options._.length) yargs.showHelp();
- } catch (error) {
- printError((error.stack || error).toString())
- }
-}
+ try {
+ let options = await argsToOpts();
+ if (!options._.length) yargs.showHelp();
+ } catch (error) {
+ printError((error.stack || error).toString());
+ }
+};
-run();
\ No newline at end of file
+run();
diff --git a/packages/cli/src/init.ts b/packages/cli/src/init.ts
index 779b1f362..95e223dbe 100644
--- a/packages/cli/src/init.ts
+++ b/packages/cli/src/init.ts
@@ -8,203 +8,210 @@ import template from 'lodash.template';
import { version } from '../package.json';
interface InitOptions {
- license?: string;
- name?: string;
- components?: boolean;
- blocks?: boolean;
- i18n?: boolean;
- verbose?: boolean,
- rName?: string,
- user?: string,
- yes?: boolean,
-};
+ license?: string;
+ name?: string;
+ components?: boolean;
+ blocks?: boolean;
+ i18n?: boolean;
+ verbose?: boolean;
+ rName?: string;
+ user?: string;
+ yes?: boolean;
+}
const tmpPath = './template';
const rootPath = process.cwd();
-const getName = (str: string) => str
+const getName = (str: string) =>
+ str
.replace(/\_/g, '-')
.split('-')
- .filter(i => i)
- .map(i => i[0].toUpperCase() + i.slice(1))
+ .filter((i) => i)
+ .map((i) => i[0].toUpperCase() + i.slice(1))
.join(' ');
const getTemplateFileContent = (pth: string) => {
- const pt = path.resolve(__dirname, `${tmpPath}/${pth}`);
- return fs.readFileSync(pt, 'utf8');
+ const pt = path.resolve(__dirname, `${tmpPath}/${pth}`);
+ return fs.readFileSync(pt, 'utf8');
};
const resolveRoot = (pth: string) => {
- return path.resolve(rootPath, pth);
+ return path.resolve(rootPath, pth);
};
const resolveLocal = (pth: string) => {
- return path.resolve(__dirname, `${tmpPath}/${pth}`);
+ return path.resolve(__dirname, `${tmpPath}/${pth}`);
};
const createSourceFiles = async (opts: InitOptions = {}) => {
- const rdmSrc = getTemplateFileContent('README.md')
- const rdmDst = resolveRoot('README.md');
- const indxSrc = getTemplateFileContent('src/index.js');
- const indxDst = resolveRoot('src/index.js');
- const indexCnt = getTemplateFileContent('_index.html');
- const indexDst = resolveRoot('_index.html');
- const license = spdxLicenseList[opts.license];
- const licenseTxt = license && (license.licenseText || '')
- .replace('
', `${new Date().getFullYear()}-current`)
- .replace('', opts.name);
- ensureDir(indxDst);
- // write src/_index.html
- fs.writeFileSync(indxDst, template(indxSrc)(opts).trim());
- // write _index.html
- fs.writeFileSync(indexDst, template(indexCnt)(opts));
- // Write README.md
- fs.writeFileSync(rdmDst, template(rdmSrc)(opts));
- // write LICENSE
- licenseTxt && fs.writeFileSync(resolveRoot('LICENSE'), licenseTxt);
- // Copy files
- fs.copyFileSync(resolveLocal('.gitignore-t'), resolveRoot('.gitignore'));
- fs.copyFileSync(resolveLocal('.npmignore-t'), resolveRoot('.npmignore'));
- fs.copyFileSync(resolveLocal('tsconfig.json'), resolveRoot('tsconfig.json'));
+ const rdmSrc = getTemplateFileContent('README.md');
+ const rdmDst = resolveRoot('README.md');
+ const indxSrc = getTemplateFileContent('src/index.js');
+ const indxDst = resolveRoot('src/index.js');
+ const indexCnt = getTemplateFileContent('_index.html');
+ const indexDst = resolveRoot('_index.html');
+ const license = spdxLicenseList[opts.license];
+ const licenseTxt =
+ license &&
+ (license.licenseText || '')
+ .replace('', `${new Date().getFullYear()}-current`)
+ .replace('', opts.name);
+ ensureDir(indxDst);
+ // write src/_index.html
+ fs.writeFileSync(indxDst, template(indxSrc)(opts).trim());
+ // write _index.html
+ fs.writeFileSync(indexDst, template(indexCnt)(opts));
+ // Write README.md
+ fs.writeFileSync(rdmDst, template(rdmSrc)(opts));
+ // write LICENSE
+ licenseTxt && fs.writeFileSync(resolveRoot('LICENSE'), licenseTxt);
+ // Copy files
+ fs.copyFileSync(resolveLocal('.gitignore-t'), resolveRoot('.gitignore'));
+ fs.copyFileSync(resolveLocal('.npmignore-t'), resolveRoot('.npmignore'));
+ fs.copyFileSync(resolveLocal('tsconfig.json'), resolveRoot('tsconfig.json'));
};
const createFileComponents = (opts: InitOptions = {}) => {
- const filepath = 'src/components.js';
- const cmpSrc = resolveLocal(filepath);
- const cmpDst = resolveRoot(filepath);
- opts.components && fs.copyFileSync(cmpSrc, cmpDst);
+ const filepath = 'src/components.js';
+ const cmpSrc = resolveLocal(filepath);
+ const cmpDst = resolveRoot(filepath);
+ opts.components && fs.copyFileSync(cmpSrc, cmpDst);
};
const createFileBlocks = (opts: InitOptions = {}) => {
- const filepath = 'src/blocks.js';
- const blkSrc = resolveLocal(filepath);
- const blkDst = resolveRoot(filepath);
- opts.blocks && fs.copyFileSync(blkSrc, blkDst);
+ const filepath = 'src/blocks.js';
+ const blkSrc = resolveLocal(filepath);
+ const blkDst = resolveRoot(filepath);
+ opts.blocks && fs.copyFileSync(blkSrc, blkDst);
};
const createI18n = (opts = {}) => {
- const enPath = 'src/locale/en.js';
- const tmpEn = getTemplateFileContent(enPath);
- const dstEn = resolveRoot(enPath);
- ensureDir(dstEn);
- fs.writeFileSync(dstEn, template(tmpEn)(opts));
+ const enPath = 'src/locale/en.js';
+ const tmpEn = getTemplateFileContent(enPath);
+ const dstEn = resolveRoot(enPath);
+ ensureDir(dstEn);
+ fs.writeFileSync(dstEn, template(tmpEn)(opts));
};
const createPackage = (opts = {}) => {
- const filepath = 'package.json';
- const cnt = getTemplateFileContent(filepath);
- const dst = resolveRoot(filepath);
- fs.writeFileSync(dst, template(cnt)({
- ...opts,
- version,
- }));
+ const filepath = 'package.json';
+ const cnt = getTemplateFileContent(filepath);
+ const dst = resolveRoot(filepath);
+ fs.writeFileSync(
+ dst,
+ template(cnt)({
+ ...opts,
+ version,
+ }),
+ );
};
-const checkBoolean = value => value && value !== 'false' ? true : false;
-
-export const initPlugin = async(opts: InitOptions = {}) => {
- printRow('Start project creation...');
- opts.components = checkBoolean(opts.components);
- opts.blocks = checkBoolean(opts.blocks);
- opts.i18n = checkBoolean(opts.i18n);
-
- const tasks = new Listr([
- {
- title: 'Creating initial source files',
- task: () => createSourceFiles(opts),
- }, {
- title: 'Creating custom Component Type file',
- task: () => createFileComponents(opts),
- enabled: () => opts.components,
- }, {
- title: 'Creating Blocks file',
- task: () => createFileBlocks(opts),
- enabled: () => opts.blocks,
- }, {
- title: 'Creating i18n structure',
- task: () => createI18n(opts),
- enabled: () => opts.i18n,
- }, {
- title: 'Update package.json',
- task: () => createPackage(opts),
- },
- ]);
- await tasks.run();
-}
+const checkBoolean = (value) => (value && value !== 'false' ? true : false);
+
+export const initPlugin = async (opts: InitOptions = {}) => {
+ printRow('Start project creation...');
+ opts.components = checkBoolean(opts.components);
+ opts.blocks = checkBoolean(opts.blocks);
+ opts.i18n = checkBoolean(opts.i18n);
+
+ const tasks = new Listr([
+ {
+ title: 'Creating initial source files',
+ task: () => createSourceFiles(opts),
+ },
+ {
+ title: 'Creating custom Component Type file',
+ task: () => createFileComponents(opts),
+ enabled: () => opts.components,
+ },
+ {
+ title: 'Creating Blocks file',
+ task: () => createFileBlocks(opts),
+ enabled: () => opts.blocks,
+ },
+ {
+ title: 'Creating i18n structure',
+ task: () => createI18n(opts),
+ enabled: () => opts.i18n,
+ },
+ {
+ title: 'Update package.json',
+ task: () => createPackage(opts),
+ },
+ ]);
+ await tasks.run();
+};
export default async (opts: InitOptions = {}) => {
- const rootDir = path.basename(process.cwd());
- const questions = [];
- const {
- verbose,
- name,
- rName,
- user,
- yes,
- components,
- blocks,
- i18n,
- license,
- } = opts;
- let results = {
- name: name || getName(rootDir),
- rName: rName || rootDir,
- user: user || 'YOUR-USERNAME',
- components: isUndefined(components) ? true : components,
- blocks: isUndefined(blocks) ? true : blocks,
- i18n: isUndefined(i18n) ? true : i18n,
- license: license || 'MIT',
- };
- printRow(`Init the project${verbose ? ' (verbose)' : ''}...`);
-
- if (!yes) {
- !name && questions.push({
- name: 'name',
- message: 'Name of the project',
- default: results.name,
- });
- !rName && questions.push({
- name: 'rName',
- message: 'Repository name (used also as the plugin name)',
- default: results.rName,
- });
- !user && questions.push({
- name: 'user',
- message: 'Repository username (eg. on GitHub/Bitbucket)',
- default: results.user,
- });
- isUndefined(components) && questions.push({
- type: 'boolean',
- name: 'components',
- message: 'Will you need to add custom Component Types?',
- default: results.components,
- });
- isUndefined(blocks) && questions.push({
- type: 'boolean',
- name: 'blocks',
- message: 'Will you need to add Blocks?',
- default: results.blocks,
- });
- isUndefined(i18n) && questions.push({
- type: 'boolean',
- name: 'i18n',
- message: 'Do you want to setup i18n structure in this plugin?',
- default: results.i18n,
- });
- !license && questions.push({
- name: 'license',
- message: 'License of the project',
- default: results.license,
- });
- }
-
- const answers = await inquirer.prompt(questions);
- results = {
- ...results,
- ...answers,
- }
-
- verbose && log({ results, opts });
- await initPlugin(results);
- printRow('Project created! Happy coding');
-}
\ No newline at end of file
+ const rootDir = path.basename(process.cwd());
+ const questions = [];
+ const { verbose, name, rName, user, yes, components, blocks, i18n, license } = opts;
+ let results = {
+ name: name || getName(rootDir),
+ rName: rName || rootDir,
+ user: user || 'YOUR-USERNAME',
+ components: isUndefined(components) ? true : components,
+ blocks: isUndefined(blocks) ? true : blocks,
+ i18n: isUndefined(i18n) ? true : i18n,
+ license: license || 'MIT',
+ };
+ printRow(`Init the project${verbose ? ' (verbose)' : ''}...`);
+
+ if (!yes) {
+ !name &&
+ questions.push({
+ name: 'name',
+ message: 'Name of the project',
+ default: results.name,
+ });
+ !rName &&
+ questions.push({
+ name: 'rName',
+ message: 'Repository name (used also as the plugin name)',
+ default: results.rName,
+ });
+ !user &&
+ questions.push({
+ name: 'user',
+ message: 'Repository username (eg. on GitHub/Bitbucket)',
+ default: results.user,
+ });
+ isUndefined(components) &&
+ questions.push({
+ type: 'boolean',
+ name: 'components',
+ message: 'Will you need to add custom Component Types?',
+ default: results.components,
+ });
+ isUndefined(blocks) &&
+ questions.push({
+ type: 'boolean',
+ name: 'blocks',
+ message: 'Will you need to add Blocks?',
+ default: results.blocks,
+ });
+ isUndefined(i18n) &&
+ questions.push({
+ type: 'boolean',
+ name: 'i18n',
+ message: 'Do you want to setup i18n structure in this plugin?',
+ default: results.i18n,
+ });
+ !license &&
+ questions.push({
+ name: 'license',
+ message: 'License of the project',
+ default: results.license,
+ });
+ }
+
+ const answers = await inquirer.prompt(questions);
+ results = {
+ ...results,
+ ...answers,
+ };
+
+ verbose && log({ results, opts });
+ await initPlugin(results);
+ printRow('Project created! Happy coding');
+};
diff --git a/packages/cli/src/main.ts b/packages/cli/src/main.ts
index 7970ff208..2cfdd3396 100644
--- a/packages/cli/src/main.ts
+++ b/packages/cli/src/main.ts
@@ -1,3 +1,3 @@
export { default as init } from './init';
export { default as build } from './build';
-export { default as serve } from './serve';
\ No newline at end of file
+export { default as serve } from './serve';
diff --git a/packages/cli/src/serve.ts b/packages/cli/src/serve.ts
index 4af037ae5..3dfba4ae9 100644
--- a/packages/cli/src/serve.ts
+++ b/packages/cli/src/serve.ts
@@ -5,9 +5,9 @@ import webpackConfig from './webpack.config';
import chalk from 'chalk';
interface ServeOptions {
- host?: string;
- port?: number;
- verbose?: boolean;
+ host?: string;
+ port?: number;
+ verbose?: boolean;
}
/**
@@ -15,34 +15,34 @@ interface ServeOptions {
* @param {Object} opts
*/
export default (opts: ServeOptions = {}) => {
- printRow('Start the development server...');
- const { host, port } = opts;
- const isVerb = opts.verbose;
- const resultWebpackConf = {
- ...webpackConfig({ args: buildWebpackArgs(opts), cmdOpts: opts }),
- ...normalizeJsonOpt(opts, 'webpack'),
- };
- const devServerConf = {
- ...resultWebpackConf.devServer,
- open: true,
- ...normalizeJsonOpt(opts, 'devServer'),
- };
+ printRow('Start the development server...');
+ const { host, port } = opts;
+ const isVerb = opts.verbose;
+ const resultWebpackConf = {
+ ...webpackConfig({ args: buildWebpackArgs(opts), cmdOpts: opts }),
+ ...normalizeJsonOpt(opts, 'webpack'),
+ };
+ const devServerConf = {
+ ...resultWebpackConf.devServer,
+ open: true,
+ ...normalizeJsonOpt(opts, 'devServer'),
+ };
- if (host !== 'localhost') {
- devServerConf.host = host;
- }
+ if (host !== 'localhost') {
+ devServerConf.host = host;
+ }
- if (port !== 8080) {
- devServerConf.port = port;
- }
+ if (port !== 8080) {
+ devServerConf.port = port;
+ }
- if (isVerb) {
- log(chalk.yellow('Server config:\n'), opts, '\n');
- log(chalk.yellow('DevServer config:\n'), devServerConf, '\n');
- }
+ if (isVerb) {
+ log(chalk.yellow('Server config:\n'), opts, '\n');
+ log(chalk.yellow('DevServer config:\n'), devServerConf, '\n');
+ }
- const compiler = webpack(resultWebpackConf);
- const server = new webpackDevServer(devServerConf, compiler);
+ const compiler = webpack(resultWebpackConf);
+ const server = new webpackDevServer(devServerConf, compiler);
- server.start();
-};
\ No newline at end of file
+ server.start();
+};
diff --git a/packages/cli/src/template/README.md b/packages/cli/src/template/README.md
index adb56eab6..311b16fec 100644
--- a/packages/cli/src/template/README.md
+++ b/packages/cli/src/template/README.md
@@ -11,7 +11,7 @@ Below, you'll find the necessary HTML, CSS, and JavaScript. Copy and paste this
### HTML
```html
-
+
@@ -22,7 +22,7 @@ Below, you'll find the necessary HTML, CSS, and JavaScript. Copy and paste this
```js
const editor = grapesjs.init({
- container: '#gjs',
+ container: '#gjs',
height: '100%',
fromElement: true,
storageManager: false,
@@ -33,7 +33,8 @@ const editor = grapesjs.init({
### CSS
```css
-body, html {
+body,
+html {
margin: 0;
height: 100%;
}
@@ -41,39 +42,37 @@ body, html {
## Summary
-* Plugin name: `<%= rName %>`
-* Components
- * `component-id-1`
- * `component-id-2`
- * ...
-* Blocks
- * `block-id-1`
- * `block-id-2`
- * ...
+- Plugin name: `<%= rName %>`
+- Components
+ - `component-id-1`
+ - `component-id-2`
+ - ...
+- Blocks
+ - `block-id-1`
+ - `block-id-2`
+ - ...
## Options
-| Option | Description | Default |
-|-|-|-|
+| Option | Description | Default |
+| --------- | ------------------ | --------------- |
| `option1` | Description option | `default value` |
## Download
-* CDN
- * `https://unpkg.com/<%= rName %>`
-* NPM
- * `npm i <%= rName %>`
-* GIT
- * `git clone https://github.com/<%= user %>/<%= rName %>.git`
-
-
+- CDN
+ - `https://unpkg.com/<%= rName %>`
+- NPM
+ - `npm i <%= rName %>`
+- GIT
+ - `git clone https://github.com/<%= user %>/<%= rName %>.git`
## Usage
Directly in the browser
```html
-
+
@@ -81,12 +80,14 @@ Directly in the browser
```
diff --git a/packages/cli/src/template/src/blocks.js b/packages/cli/src/template/src/blocks.js
index e8c0e83b2..9a889ec4d 100644
--- a/packages/cli/src/template/src/blocks.js
+++ b/packages/cli/src/template/src/blocks.js
@@ -6,4 +6,4 @@ export default (editor, opts = {}) => {
content: { type: 'MY-COMPONENT' },
// media: '... ',
});
-}
+};
diff --git a/packages/cli/src/template/src/components.js b/packages/cli/src/template/src/components.js
index 70c691791..78ef54ca0 100644
--- a/packages/cli/src/template/src/components.js
+++ b/packages/cli/src/template/src/components.js
@@ -7,8 +7,6 @@ export default (editor, opts = {}) => {
// Default props
},
},
- view: {
-
- },
+ view: {},
});
};
diff --git a/packages/cli/src/template/tsconfig.json b/packages/cli/src/template/tsconfig.json
index e56e47d36..f9599dd33 100644
--- a/packages/cli/src/template/tsconfig.json
+++ b/packages/cli/src/template/tsconfig.json
@@ -1,11 +1,7 @@
{
"compilerOptions": {
"target": "es5",
- "lib": [
- "dom",
- "dom.iterable",
- "esnext"
- ],
+ "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"sourceMap": true,
"skipLibCheck": true,
@@ -20,7 +16,5 @@
"isolatedModules": true,
"noEmit": false
},
- "include": [
- "src"
- ]
+ "include": ["src"]
}
diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts
index 897509836..a3dfcb4ac 100644
--- a/packages/cli/src/utils.ts
+++ b/packages/cli/src/utils.ts
@@ -11,27 +11,24 @@ export const isFunction = (value: any): value is Function => typeof value === 'f
export const isObject = (val: any) => val !== null && !Array.isArray(val) && typeof val === 'object';
-export const printRow = (str: string, {
- color = 'green',
- lineDown = 1,
-} = {}) => {
- console.log('');
- console.log(chalk[color].bold(str));
- lineDown && console.log('');
-}
+export const printRow = (str: string, { color = 'green', lineDown = 1 } = {}) => {
+ console.log('');
+ console.log(chalk[color].bold(str));
+ lineDown && console.log('');
+};
export const printError = (str: string) => {
- printRow(str, { color: 'red' });
-}
+ printRow(str, { color: 'red' });
+};
export const log = (...args: any[]) => console.log.apply(this, args);
export const ensureDir = (filePath: string) => {
- const dirname = path.dirname(filePath);
- if (fs.existsSync(dirname)) return true;
- fs.mkdirSync(dirname);
- return ensureDir(dirname);
-}
+ const dirname = path.dirname(filePath);
+ if (fs.existsSync(dirname)) return true;
+ fs.mkdirSync(dirname);
+ return ensureDir(dirname);
+};
/**
* Normalize JSON options
@@ -40,85 +37,87 @@ export const ensureDir = (filePath: string) => {
* @returns {Object}
*/
export const normalizeJsonOpt = (opts: Record, key: string) => {
- let devServerOpt = opts[key] || {};
-
- if (isString(devServerOpt)) {
- try {
- devServerOpt = JSON.parse(devServerOpt);
- } catch (e) {
- printError(`Error while parsing "${key}" option`);
- printError(e);
- devServerOpt = {}
- }
+ let devServerOpt = opts[key] || {};
+
+ if (isString(devServerOpt)) {
+ try {
+ devServerOpt = JSON.parse(devServerOpt);
+ } catch (e) {
+ printError(`Error while parsing "${key}" option`);
+ printError(e);
+ devServerOpt = {};
}
+ }
- return devServerOpt;
-}
+ return devServerOpt;
+};
export const buildWebpackArgs = (opts: Record) => {
- return {
- ...opts,
- babel: normalizeJsonOpt(opts, 'babel'),
- htmlWebpack: normalizeJsonOpt(opts, 'htmlWebpack'),
- }
-}
+ return {
+ ...opts,
+ babel: normalizeJsonOpt(opts, 'babel'),
+ htmlWebpack: normalizeJsonOpt(opts, 'htmlWebpack'),
+ };
+};
export const copyRecursiveSync = (src: string, dest: string) => {
- const exists = fs.existsSync(src);
- const isDir = exists && fs.statSync(src).isDirectory();
-
- if (isDir) {
- fs.mkdirSync(dest);
- fs.readdirSync(src).forEach((file) => {
- copyRecursiveSync(path.join(src, file), path.join(dest, file));
- });
- } else if (exists) {
- fs.copyFileSync(src, dest);
- }
+ const exists = fs.existsSync(src);
+ const isDir = exists && fs.statSync(src).isDirectory();
+
+ if (isDir) {
+ fs.mkdirSync(dest);
+ fs.readdirSync(src).forEach((file) => {
+ copyRecursiveSync(path.join(src, file), path.join(dest, file));
+ });
+ } else if (exists) {
+ fs.copyFileSync(src, dest);
+ }
};
export const isPathExists = async (path: string) => {
- try {
- await fsp.access(path);
- return true;
- } catch {
- return false;
- }
- };
-
+ try {
+ await fsp.access(path);
+ return true;
+ } catch {
+ return false;
+ }
+};
export const writeFile = async (filePath: string, data: string) => {
- try {
- const dirname = path.dirname(filePath);
- const exist = await isPathExists(dirname);
- if (!exist) {
- await fsp.mkdir(dirname, { recursive: true });
- }
-
- await fsp.writeFile(filePath, data, 'utf8');
- } catch (err) {
- throw new Error(err);
+ try {
+ const dirname = path.dirname(filePath);
+ const exist = await isPathExists(dirname);
+ if (!exist) {
+ await fsp.mkdir(dirname, { recursive: true });
}
-}
+
+ await fsp.writeFile(filePath, data, 'utf8');
+ } catch (err) {
+ throw new Error(err);
+ }
+};
export const rootResolve = (val: string) => path.resolve(process.cwd(), val);
export const originalRequire = () => {
- // @ts-ignore need this to use the original 'require.resolve' as it's replaced by webpack
- return __non_webpack_require__;
+ // @ts-ignore need this to use the original 'require.resolve' as it's replaced by webpack
+ return __non_webpack_require__;
};
export const resolve = (value: string) => {
- return originalRequire().resolve(value);
+ return originalRequire().resolve(value);
};
export const babelConfig = (opts: { targets?: string } = {}) => ({
- presets: [
- [ resolve('@babel/preset-env'), {
- targets: opts.targets,
- // useBuiltIns: 'usage', // this makes the build much bigger
- // corejs: 3,
- } ]
+ presets: [
+ [
+ resolve('@babel/preset-env'),
+ {
+ targets: opts.targets,
+ // useBuiltIns: 'usage', // this makes the build much bigger
+ // corejs: 3,
+ },
],
- plugins: [ resolve('@babel/plugin-transform-runtime') ],
-})
\ No newline at end of file
+ ],
+ plugins: [resolve('@babel/plugin-transform-runtime')],
+});
diff --git a/packages/cli/src/webpack.config.ts b/packages/cli/src/webpack.config.ts
index 72fca71e2..32069f05f 100644
--- a/packages/cli/src/webpack.config.ts
+++ b/packages/cli/src/webpack.config.ts
@@ -31,60 +31,66 @@ export default (opts: Record = {}) => {
template = index;
}
- plugins.push(new HtmlWebpackPlugin({
- inject: 'head',
- template,
- ...htmlWebpack,
- templateParameters: {
- name,
- title: name,
- gjsVersion: 'latest',
- pathGjs: '',
- pathGjsCss: '',
- ...htmlWebpack.templateParameters || {},
- },
- }));
+ plugins.push(
+ new HtmlWebpackPlugin({
+ inject: 'head',
+ template,
+ ...htmlWebpack,
+ templateParameters: {
+ name,
+ title: name,
+ gjsVersion: 'latest',
+ pathGjs: '',
+ pathGjsCss: '',
+ ...(htmlWebpack.templateParameters || {}),
+ },
+ }),
+ );
}
const outPath = path.resolve(dirCwd, args.output);
- const modulesPaths = [ 'node_modules', path.join(__dirname, '../node_modules')];
+ const modulesPaths = ['node_modules', path.join(__dirname, '../node_modules')];
let config = {
entry: path.resolve(dirCwd, args.entry),
mode: isProd ? 'production' : 'development',
devtool: isProd ? 'source-map' : 'eval',
optimization: {
- minimizer: [new TerserPlugin({
- extractComments: false,
- terserOptions: {
- compress: {
- evaluate: false, // Avoid breaking gjs scripts
+ minimizer: [
+ new TerserPlugin({
+ extractComments: false,
+ terserOptions: {
+ compress: {
+ evaluate: false, // Avoid breaking gjs scripts
+ },
+ output: {
+ comments: false,
+ quote_style: 3, // Preserve original quotes
+ preamble: banner, // banner here instead of BannerPlugin
+ },
},
- output: {
- comments: false,
- quote_style: 3, // Preserve original quotes
- preamble: banner, // banner here instead of BannerPlugin
- }
- }
- })],
+ }),
+ ],
},
output: {
- path: outPath,
- filename: 'index.js',
- library: name,
- libraryTarget: 'umd',
- globalObject: `typeof globalThis !== 'undefined' ? globalThis : (typeof window !== 'undefined' ? window : this)`,
+ path: outPath,
+ filename: 'index.js',
+ library: name,
+ libraryTarget: 'umd',
+ globalObject: `typeof globalThis !== 'undefined' ? globalThis : (typeof window !== 'undefined' ? window : this)`,
},
module: {
- rules: [{
- test: /\.tsx?$/,
- loader: resolve('ts-loader'),
- exclude: /node_modules/,
- options: {
- context: rootResolve(''),
- configFile: rootResolve('tsconfig.json'),
- }
- }, {
+ rules: [
+ {
+ test: /\.tsx?$/,
+ loader: resolve('ts-loader'),
+ exclude: /node_modules/,
+ options: {
+ context: rootResolve(''),
+ configFile: rootResolve('tsconfig.json'),
+ },
+ },
+ {
test: /\.js$/,
loader: resolve('babel-loader'),
include: /src/,
@@ -93,7 +99,8 @@ export default (opts: Record = {}) => {
cacheDirectory: true,
...args.babel,
},
- }],
+ },
+ ],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
@@ -112,11 +119,11 @@ export default (opts: Record = {}) => {
}
if (isFunction(localWebpackConf)) {
- const fnRes = localWebpackConf({ config, webpack, pkg });
- config = isObject(fnRes) ? fnRes : config;
+ const fnRes = localWebpackConf({ config, webpack, pkg });
+ config = isObject(fnRes) ? fnRes : config;
}
cmdOpts.verbose && log(chalk.yellow('Webpack config:\n'), config, '\n');
return config;
-}
+};
diff --git a/packages/cli/test/utils.spec.ts b/packages/cli/test/utils.spec.ts
index c295d918c..d01d2f75c 100644
--- a/packages/cli/test/utils.spec.ts
+++ b/packages/cli/test/utils.spec.ts
@@ -1,338 +1,359 @@
-import { isFunction, isObject, isString, isUndefined, printRow, printError, log, ensureDir, normalizeJsonOpt, buildWebpackArgs, copyRecursiveSync, babelConfig, originalRequire, resolve, rootResolve } from "../src/utils";
+import {
+ isFunction,
+ isObject,
+ isString,
+ isUndefined,
+ printRow,
+ printError,
+ log,
+ ensureDir,
+ normalizeJsonOpt,
+ buildWebpackArgs,
+ copyRecursiveSync,
+ babelConfig,
+ originalRequire,
+ resolve,
+ rootResolve,
+} from '../src/utils';
import chalk from 'chalk';
import fs from 'fs';
import path from 'path';
import * as process from 'process';
const typeTestValues = {
- undefinedValue: undefined,
- nullValue: null,
- stringValue: 'hello',
- emptyObject: {},
- nonEmptyObject: { key: 'value' },
- emptyArray: [],
- functionValue: () => { },
- numberValue: 42,
- booleanValue: true,
- dateValue: new Date(),
+ undefinedValue: undefined,
+ nullValue: null,
+ stringValue: 'hello',
+ emptyObject: {},
+ nonEmptyObject: { key: 'value' },
+ emptyArray: [],
+ functionValue: () => {},
+ numberValue: 42,
+ booleanValue: true,
+ dateValue: new Date(),
};
function runTypeCheck(typeCheckFunction: (value: any) => boolean) {
- const keysWithPassingTypeChecks = Object.keys(typeTestValues).filter(key => {
- const value = typeTestValues[key];
- return typeCheckFunction(value);
- });
+ const keysWithPassingTypeChecks = Object.keys(typeTestValues).filter((key) => {
+ const value = typeTestValues[key];
+ return typeCheckFunction(value);
+ });
- return keysWithPassingTypeChecks;
+ return keysWithPassingTypeChecks;
}
jest.mock('fs');
jest.mock('fs/promises');
describe('utils', () => {
- afterEach(() => {
- jest.clearAllMocks();
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ describe('isString', () => {
+ it('should correctly identify strings', () => {
+ const result = runTypeCheck(isString);
+ expect(result).toEqual(['stringValue']);
});
+ });
- describe('isString', () => {
- it('should correctly identify strings', () => {
- const result = runTypeCheck(isString);
- expect(result).toEqual(['stringValue']);
- });
+ describe('isUndefined', () => {
+ it('should correctly identify undefined values', () => {
+ const result = runTypeCheck(isUndefined);
+ expect(result).toEqual(['undefinedValue']);
});
+ });
- describe('isUndefined', () => {
- it('should correctly identify undefined values', () => {
- const result = runTypeCheck(isUndefined);
- expect(result).toEqual(['undefinedValue']);
- });
+ describe('isFunction', () => {
+ it('should correctly identify functions', () => {
+ const result = runTypeCheck(isFunction);
+ expect(result).toEqual(['functionValue']);
});
+ });
- describe('isFunction', () => {
- it('should correctly identify functions', () => {
- const result = runTypeCheck(isFunction);
- expect(result).toEqual(['functionValue']);
- });
+ describe('isObject', () => {
+ it('should correctly identify objects', () => {
+ const result = runTypeCheck(isObject);
+ expect(result).toEqual(['emptyObject', 'nonEmptyObject', 'dateValue']);
});
+ });
+
+ describe('printRow', () => {
+ // TODO: We should refactor the function to make lineDown a boolean not a number
+ it('should console.log the given string with the specified color and line breaks', () => {
+ const str = 'Test string';
+ const color = 'blue';
+ const lineDown = 1;
+
+ console.log = jest.fn();
+
+ printRow(str, { color, lineDown });
- describe('isObject', () => {
- it('should correctly identify objects', () => {
- const result = runTypeCheck(isObject);
- expect(result).toEqual(['emptyObject', 'nonEmptyObject', 'dateValue']);
- });
+ expect(console.log).toHaveBeenCalledTimes(3); // 1 for empty line, 1 for colored string, 1 for line break
+ expect(console.log.mock.calls[1][0]).toEqual(chalk[color].bold(str));
});
+ it('should not add a line break if lineDown is false', () => {
+ const str = 'Test string';
+ const color = 'green';
+ const lineDown = 0;
- describe('printRow', () => {
- // TODO: We should refactor the function to make lineDown a boolean not a number
- it('should console.log the given string with the specified color and line breaks', () => {
- const str = 'Test string';
- const color = 'blue';
- const lineDown = 1;
+ console.log = jest.fn();
- console.log = jest.fn();
+ printRow(str, { color, lineDown });
- printRow(str, { color, lineDown });
+ expect(console.log).toHaveBeenCalledTimes(2); // 1 for empty line, 1 for colored string
+ });
+ });
+
+ describe('printError', () => {
+ it('should print the given string in red', () => {
+ const str = 'Error message';
- expect(console.log).toHaveBeenCalledTimes(3); // 1 for empty line, 1 for colored string, 1 for line break
- expect(console.log.mock.calls[1][0]).toEqual(chalk[color].bold(str));
- });
+ console.log = jest.fn();
- it('should not add a line break if lineDown is false', () => {
- const str = 'Test string';
- const color = 'green';
- const lineDown = 0;
+ printError(str);
+
+ expect(console.log).toHaveBeenCalledTimes(3); // 1 for empty line, 1 for red string, 1 for line break
+ expect(console.log.mock.calls[1][0]).toEqual(chalk.red.bold(str));
+ });
+ });
- console.log = jest.fn();
+ describe('log', () => {
+ it('should call console.log with the given arguments', () => {
+ const arg1 = 'Argument 1';
+ const arg2 = 'Argument 2';
- printRow(str, { color, lineDown });
+ console.log = jest.fn();
- expect(console.log).toHaveBeenCalledTimes(2); // 1 for empty line, 1 for colored string
- });
+ log(arg1, arg2);
+
+ expect(console.log).toHaveBeenCalledWith(arg1, arg2);
});
+ });
- describe('printError', () => {
- it('should print the given string in red', () => {
- const str = 'Error message';
+ describe('ensureDir', () => {
+ it('should return true when the directory already exists', () => {
+ (fs.existsSync as jest.Mock).mockReturnValue(true);
- console.log = jest.fn();
+ const result = ensureDir('/path/to/file.txt');
+ expect(result).toBe(true);
+ expect(fs.existsSync).toHaveBeenCalledWith('/path/to');
+ expect(fs.mkdirSync).not.toHaveBeenCalled();
+ });
- printError(str);
+ it('should create the directory when it does not exist', () => {
+ (fs.existsSync as jest.Mock).mockReturnValueOnce(false).mockReturnValueOnce(true);
- expect(console.log).toHaveBeenCalledTimes(3); // 1 for empty line, 1 for red string, 1 for line break
- expect(console.log.mock.calls[1][0]).toEqual(chalk.red.bold(str));
- });
+ const result = ensureDir('/path/to/file.txt');
+ expect(result).toBe(true);
+ expect(fs.existsSync).toHaveBeenCalledWith('/path/to');
+ expect(fs.mkdirSync).toHaveBeenCalledWith('/path/to');
});
- describe('log', () => {
- it('should call console.log with the given arguments', () => {
- const arg1 = 'Argument 1';
- const arg2 = 'Argument 2';
+ it('should create parent directories recursively when they do not exist', () => {
+ (fs.existsSync as jest.Mock)
+ .mockReturnValueOnce(false) // Check /path/to (does not exist)
+ .mockReturnValueOnce(false) // Check /path (does not exist)
+ .mockReturnValueOnce(true); // Check / (root, exists)
- console.log = jest.fn();
+ const result = ensureDir('/path/to/file.txt');
+ expect(result).toBe(true);
+ expect(fs.existsSync).toHaveBeenCalledTimes(3); // /path/to, /path, /
+ expect(fs.mkdirSync).toHaveBeenCalledTimes(2); // /path, /path/to
+ });
+ });
- log(arg1, arg2);
+ describe('normalizeJsonOpt', () => {
+ it('should return the object if the option is already an object', () => {
+ const opts = { babel: { presets: ['@babel/preset-env'] } };
+ const result = normalizeJsonOpt(opts, 'babel');
+ expect(result).toEqual(opts.babel);
+ });
- expect(console.log).toHaveBeenCalledWith(arg1, arg2);
- });
+ it('should parse and return the object if the option is a valid JSON string', () => {
+ const opts = { babel: '{"presets":["@babel/preset-env"]}' };
+ const result = normalizeJsonOpt(opts, 'babel');
+ expect(result).toEqual({ presets: ['@babel/preset-env'] });
});
- describe('ensureDir', () => {
- it('should return true when the directory already exists', () => {
- (fs.existsSync as jest.Mock).mockReturnValue(true);
-
- const result = ensureDir('/path/to/file.txt');
- expect(result).toBe(true);
- expect(fs.existsSync).toHaveBeenCalledWith('/path/to');
- expect(fs.mkdirSync).not.toHaveBeenCalled();
- });
-
- it('should create the directory when it does not exist', () => {
- (fs.existsSync as jest.Mock).mockReturnValueOnce(false).mockReturnValueOnce(true);
-
- const result = ensureDir('/path/to/file.txt');
- expect(result).toBe(true);
- expect(fs.existsSync).toHaveBeenCalledWith('/path/to');
- expect(fs.mkdirSync).toHaveBeenCalledWith('/path/to');
- });
-
- it('should create parent directories recursively when they do not exist', () => {
- (fs.existsSync as jest.Mock)
- .mockReturnValueOnce(false) // Check /path/to (does not exist)
- .mockReturnValueOnce(false) // Check /path (does not exist)
- .mockReturnValueOnce(true); // Check / (root, exists)
-
- const result = ensureDir('/path/to/file.txt');
- expect(result).toBe(true);
- expect(fs.existsSync).toHaveBeenCalledTimes(3); // /path/to, /path, /
- expect(fs.mkdirSync).toHaveBeenCalledTimes(2); // /path, /path/to
- });
+ it('should return an empty object if the option is an invalid JSON string', () => {
+ const opts = { babel: '{"presets":["@babel/preset-env"]' }; // Invalid JSON
+ const result = normalizeJsonOpt(opts, 'babel');
+ expect(result).toEqual({});
});
- describe('normalizeJsonOpt', () => {
- it('should return the object if the option is already an object', () => {
- const opts = { babel: { presets: ['@babel/preset-env'] } };
- const result = normalizeJsonOpt(opts, 'babel');
- expect(result).toEqual(opts.babel);
- });
-
- it('should parse and return the object if the option is a valid JSON string', () => {
- const opts = { babel: '{"presets":["@babel/preset-env"]}' };
- const result = normalizeJsonOpt(opts, 'babel');
- expect(result).toEqual({ presets: ['@babel/preset-env'] });
- });
-
- it('should return an empty object if the option is an invalid JSON string', () => {
- const opts = { babel: '{"presets":["@babel/preset-env"]' }; // Invalid JSON
- const result = normalizeJsonOpt(opts, 'babel');
- expect(result).toEqual({});
- });
-
- it('should return an empty object if the option is not provided', () => {
- const opts = {};
- const result = normalizeJsonOpt(opts, 'babel');
- expect(result).toEqual({});
- });
+ it('should return an empty object if the option is not provided', () => {
+ const opts = {};
+ const result = normalizeJsonOpt(opts, 'babel');
+ expect(result).toEqual({});
+ });
+ });
+
+ describe('buildWebpackArgs', () => {
+ it('should return the options with normalized JSON options for babel and htmlWebpack', () => {
+ const opts = {
+ babel: '{"presets":["@babel/preset-env"]}',
+ htmlWebpack: '{"template":"./src/index.html"}',
+ otherOption: 'someValue',
+ };
+
+ const result = buildWebpackArgs(opts);
+ expect(result).toEqual({
+ babel: { presets: ['@babel/preset-env'] },
+ htmlWebpack: { template: './src/index.html' },
+ otherOption: 'someValue',
+ });
});
- describe('buildWebpackArgs', () => {
- it('should return the options with normalized JSON options for babel and htmlWebpack', () => {
- const opts = {
- babel: '{"presets":["@babel/preset-env"]}',
- htmlWebpack: '{"template":"./src/index.html"}',
- otherOption: 'someValue'
- };
-
- const result = buildWebpackArgs(opts);
- expect(result).toEqual({
- babel: { presets: ['@babel/preset-env'] },
- htmlWebpack: { template: './src/index.html' },
- otherOption: 'someValue',
- });
- });
-
- it('should return empty objects for babel and htmlWebpack if they are invalid JSON strings', () => {
- const opts = {
- babel: '{"presets":["@babel/preset-env"]', // Invalid JSON
- htmlWebpack: '{"template":"./src/index.html', // Invalid JSON
- };
-
- const result = buildWebpackArgs(opts);
- expect(result).toEqual({
- babel: {},
- htmlWebpack: {},
- });
- });
-
- it('should return the original objects if babel and htmlWebpack are already objects', () => {
- const opts = {
- babel: { presets: ['@babel/preset-env'] },
- htmlWebpack: { template: './src/index.html' },
- };
-
- const result = buildWebpackArgs(opts);
- expect(result).toEqual({
- babel: opts.babel,
- htmlWebpack: opts.htmlWebpack,
- });
- });
-
- it('should handle missing babel and htmlWebpack keys gracefully', () => {
- const opts = { otherOption: 'someValue' };
-
- const result = buildWebpackArgs(opts);
- expect(result).toEqual({
- babel: {},
- htmlWebpack: {},
- otherOption: 'someValue',
- });
- });
+ it('should return empty objects for babel and htmlWebpack if they are invalid JSON strings', () => {
+ const opts = {
+ babel: '{"presets":["@babel/preset-env"]', // Invalid JSON
+ htmlWebpack: '{"template":"./src/index.html', // Invalid JSON
+ };
+
+ const result = buildWebpackArgs(opts);
+ expect(result).toEqual({
+ babel: {},
+ htmlWebpack: {},
+ });
});
- describe('copyRecursiveSync', () => {
- // TODO: Maybe this test case is a bit complex and we should think of an easier solution
- it('should copy a directory and its contents recursively', () => {
- /**
- * First call: Mock as a directory with two files
- * Subsequent calls: Mock as a file
- */
- const existsSyncMock = (fs.existsSync as jest.Mock).mockReturnValue(true);
- const statSyncMock = (fs.statSync as jest.Mock).mockReturnValueOnce({ isDirectory: () => true }).mockReturnValue({ isDirectory: () => false });
- const readdirSyncMock = (fs.readdirSync as jest.Mock).mockReturnValueOnce(['file1.txt', 'file2.txt']).mockReturnValue([]);
- const copyFileSyncMock = (fs.copyFileSync as jest.Mock).mockImplementation(() => { });
-
- copyRecursiveSync('/src', '/dest');
-
- expect(existsSyncMock).toHaveBeenCalledWith('/src');
- expect(statSyncMock).toHaveBeenCalledWith('/src');
- expect(fs.mkdirSync).toHaveBeenCalledWith('/dest');
- expect(readdirSyncMock).toHaveBeenCalledWith('/src');
- expect(copyFileSyncMock).toHaveBeenCalledWith(path.normalize('/src/file1.txt'), path.normalize('/dest/file1.txt'));
- expect(copyFileSyncMock).toHaveBeenCalledWith(path.normalize('/src/file2.txt'), path.normalize('/dest/file2.txt'));
- });
-
- it('should copy a file when source is a file', () => {
- (fs.existsSync as jest.Mock).mockReturnValue(true);
- (fs.statSync as jest.Mock).mockReturnValue({ isDirectory: () => false });
-
- copyRecursiveSync('/src/file.txt', '/dest/file.txt');
-
- expect(fs.existsSync).toHaveBeenCalledWith('/src/file.txt');
- expect(fs.statSync).toHaveBeenCalledWith('/src/file.txt');
- expect(fs.copyFileSync).toHaveBeenCalledWith('/src/file.txt', '/dest/file.txt');
- });
-
- // Maybe we can change the behavior to throw an error if the `src` doesn't exist
- it('should do nothing when source does not exist', () => {
- (fs.existsSync as jest.Mock).mockReturnValue(false);
-
- copyRecursiveSync('/src/file.txt', '/dest/file.txt');
-
- expect(fs.existsSync).toHaveBeenCalledWith('/src/file.txt');
- expect(fs.statSync).not.toHaveBeenCalled();
- expect(fs.mkdirSync).not.toHaveBeenCalled();
- expect(fs.copyFileSync).not.toHaveBeenCalled();
- });
+ it('should return the original objects if babel and htmlWebpack are already objects', () => {
+ const opts = {
+ babel: { presets: ['@babel/preset-env'] },
+ htmlWebpack: { template: './src/index.html' },
+ };
+
+ const result = buildWebpackArgs(opts);
+ expect(result).toEqual({
+ babel: opts.babel,
+ htmlWebpack: opts.htmlWebpack,
+ });
});
- describe('rootResolve', () => {
- it('should resolve a relative path to an absolute path', () => {
- const result = rootResolve('src/index.js');
+ it('should handle missing babel and htmlWebpack keys gracefully', () => {
+ const opts = { otherOption: 'someValue' };
- expect(result).toBe(path.join(process.cwd(), 'src/index.js'));
- });
+ const result = buildWebpackArgs(opts);
+ expect(result).toEqual({
+ babel: {},
+ htmlWebpack: {},
+ otherOption: 'someValue',
+ });
+ });
+ });
+
+ describe('copyRecursiveSync', () => {
+ // TODO: Maybe this test case is a bit complex and we should think of an easier solution
+ it('should copy a directory and its contents recursively', () => {
+ /**
+ * First call: Mock as a directory with two files
+ * Subsequent calls: Mock as a file
+ */
+ const existsSyncMock = (fs.existsSync as jest.Mock).mockReturnValue(true);
+ const statSyncMock = (fs.statSync as jest.Mock)
+ .mockReturnValueOnce({ isDirectory: () => true })
+ .mockReturnValue({ isDirectory: () => false });
+ const readdirSyncMock = (fs.readdirSync as jest.Mock)
+ .mockReturnValueOnce(['file1.txt', 'file2.txt'])
+ .mockReturnValue([]);
+ const copyFileSyncMock = (fs.copyFileSync as jest.Mock).mockImplementation(() => {});
+
+ copyRecursiveSync('/src', '/dest');
+
+ expect(existsSyncMock).toHaveBeenCalledWith('/src');
+ expect(statSyncMock).toHaveBeenCalledWith('/src');
+ expect(fs.mkdirSync).toHaveBeenCalledWith('/dest');
+ expect(readdirSyncMock).toHaveBeenCalledWith('/src');
+ expect(copyFileSyncMock).toHaveBeenCalledWith(
+ path.normalize('/src/file1.txt'),
+ path.normalize('/dest/file1.txt'),
+ );
+ expect(copyFileSyncMock).toHaveBeenCalledWith(
+ path.normalize('/src/file2.txt'),
+ path.normalize('/dest/file2.txt'),
+ );
});
- describe('originalRequire', () => {
- it('should return the original require.resolve function', () => {
- const originalRequireMock = jest.fn();
- global.__non_webpack_require__ = originalRequireMock;
+ it('should copy a file when source is a file', () => {
+ (fs.existsSync as jest.Mock).mockReturnValue(true);
+ (fs.statSync as jest.Mock).mockReturnValue({ isDirectory: () => false });
- const result = originalRequire();
+ copyRecursiveSync('/src/file.txt', '/dest/file.txt');
- expect(result).toBe(originalRequireMock);
- });
+ expect(fs.existsSync).toHaveBeenCalledWith('/src/file.txt');
+ expect(fs.statSync).toHaveBeenCalledWith('/src/file.txt');
+ expect(fs.copyFileSync).toHaveBeenCalledWith('/src/file.txt', '/dest/file.txt');
});
- describe('resolve', () => {
- it('should resolve a module path using the original require.resolve', () => {
- const originalRequireMock = {
- resolve: jest.fn().mockReturnValue('resolved/path'),
- };
- global.__non_webpack_require__ = originalRequireMock;
+ // Maybe we can change the behavior to throw an error if the `src` doesn't exist
+ it('should do nothing when source does not exist', () => {
+ (fs.existsSync as jest.Mock).mockReturnValue(false);
- const result = resolve('my-module');
+ copyRecursiveSync('/src/file.txt', '/dest/file.txt');
- expect(result).toBe('resolved/path');
- expect(originalRequireMock.resolve).toHaveBeenCalledWith('my-module');
- });
+ expect(fs.existsSync).toHaveBeenCalledWith('/src/file.txt');
+ expect(fs.statSync).not.toHaveBeenCalled();
+ expect(fs.mkdirSync).not.toHaveBeenCalled();
+ expect(fs.copyFileSync).not.toHaveBeenCalled();
});
+ });
+
+ describe('rootResolve', () => {
+ it('should resolve a relative path to an absolute path', () => {
+ const result = rootResolve('src/index.js');
+
+ expect(result).toBe(path.join(process.cwd(), 'src/index.js'));
+ });
+ });
+
+ describe('originalRequire', () => {
+ it('should return the original require.resolve function', () => {
+ const originalRequireMock = jest.fn();
+ global.__non_webpack_require__ = originalRequireMock;
+
+ const result = originalRequire();
+
+ expect(result).toBe(originalRequireMock);
+ });
+ });
+
+ describe('resolve', () => {
+ it('should resolve a module path using the original require.resolve', () => {
+ const originalRequireMock = {
+ resolve: jest.fn().mockReturnValue('resolved/path'),
+ };
+ global.__non_webpack_require__ = originalRequireMock;
+
+ const result = resolve('my-module');
+
+ expect(result).toBe('resolved/path');
+ expect(originalRequireMock.resolve).toHaveBeenCalledWith('my-module');
+ });
+ });
+
+ describe('babelConfig', () => {
+ afterEach(() => {
+ jest.restoreAllMocks();
+ });
+
+ it('should return a Babel configuration object with specified presets and plugins', () => {
+ const result = babelConfig();
+
+ expect(result).toEqual({
+ presets: [[resolve('@babel/preset-env'), { targets: undefined }]],
+ plugins: [resolve('@babel/plugin-transform-runtime')],
+ });
+ });
+
+ it('should include the specified targets in the Babel configuration', () => {
+ const result = babelConfig({ targets: 'node 14' });
- describe('babelConfig', () => {
- afterEach(() => {
- jest.restoreAllMocks();
- });
-
- it('should return a Babel configuration object with specified presets and plugins', () => {
- const result = babelConfig();
-
- expect(result).toEqual({
- presets: [
- [resolve('@babel/preset-env'), { targets: undefined }],
- ],
- plugins: [resolve('@babel/plugin-transform-runtime')],
- });
- });
-
- it('should include the specified targets in the Babel configuration', () => {
- const result = babelConfig({ targets: 'node 14' });
-
- expect(result).toEqual({
- presets: [
- [resolve('@babel/preset-env'), { targets: 'node 14' }],
- ],
- plugins: [resolve('@babel/plugin-transform-runtime')],
- });
- });
+ expect(result).toEqual({
+ presets: [[resolve('@babel/preset-env'), { targets: 'node 14' }]],
+ plugins: [resolve('@babel/plugin-transform-runtime')],
+ });
});
-});
\ No newline at end of file
+ });
+});
diff --git a/packages/cli/webpack.cli.ts b/packages/cli/webpack.cli.ts
index 75f07d570..3ef6c994f 100644
--- a/packages/cli/webpack.cli.ts
+++ b/packages/cli/webpack.cli.ts
@@ -56,9 +56,7 @@ const config: Configuration = {
}),
],
externalsPresets: { node: true },
- externals: [
- NodeExternals(),
- ],
+ externals: [NodeExternals()],
};
export default config;