diff --git a/README.md b/README.md index 882f96a..b43bb59 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,10 @@ chrome-extension-cli my-extension --override-page=history // Override Histo Creates a Panel inside developer tools. +#### `chrome-extension-cli my-extension --no-cross-browser` + +The generated project will not be cross-browser compatible by default. + ## Contributing See the [contribution guide](CONTRIBUTING.md) and join the contributors! diff --git a/config/webpack.common.js b/config/webpack.common.js index 52b29a5..b4680f1 100644 --- a/config/webpack.common.js +++ b/config/webpack.common.js @@ -6,60 +6,86 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const PATHS = require('./paths'); -// To re-use webpack configuration across templates, -// CLI maintains a common webpack configuration file - `webpack.common.js`. -// Whenever user creates an extension, CLI adds `webpack.common.js` file -// in template's `config` folder -const common = { - output: { - // the build folder to output bundles and assets in. - path: PATHS.build, - // the filename template for entry chunks - filename: '[name].js', - }, - devtool: 'source-map', - stats: { - all: false, - errors: true, - builtAt: true, - }, - module: { - rules: [ - // Help webpack in understanding CSS files imported in .js files - { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader'], - }, - // Check for images imported in .js files and - { - test: /\.(png|jpe?g|gif)$/i, - use: [ - { - loader: 'file-loader', - options: { - outputPath: 'images', - name: '[name].[ext]', +module.exports = env => { + // To re-use webpack configuration across templates, + // CLI maintains a common webpack configuration file - `webpack.common.js`. + // Whenever user creates an extension, CLI adds `webpack.common.js` file + // in template's `config` folder + const crossBrowser = (env.CROSS_BROWSER === 'true'); + const common = { + output: { + // the build folder to output bundles and assets in. + path: PATHS.build, + // the filename template for entry chunks + filename: '[name].js', + }, + devtool: 'source-map', + stats: { + all: false, + errors: true, + builtAt: true, + }, + module: { + rules: [ + // Help webpack in understanding CSS files imported in .js files + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, 'css-loader'], + }, + // Check for images imported in .js files and + { + test: /\.(png|jpe?g|gif)$/i, + use: [ + { + loader: 'file-loader', + options: { + outputPath: 'images', + name: '[name].[ext]', + }, }, - }, - ], - }, + ], + }, + ], + }, + plugins: [ + // Print file sizes + new SizePlugin(), + // Copy static assets from `public` folder to `build` folder, except .html files + new CopyWebpackPlugin([ + { + from: '**/*', + context: 'public', + ignore: ['*.html'] + }, + ]), + // copy static .html files from `public` folder to `build` folder, and replace the `<%= browser-polyfill %>` variable depending on config + new CopyWebpackPlugin([ + { + from: '**/*.html', + context: 'public', + transform: (content) => { + const polyfill = ''; + if(crossBrowser) { + return content.toString().replace('<%= browser-polyfill %>', polyfill); + } + return content.toString().replace('<%= browser-polyfill %>', ''); + } + }, + ]), + // Extract CSS into separate files + new MiniCssExtractPlugin({ + filename: '[name].css', + }), ], - }, - plugins: [ - // Print file sizes - new SizePlugin(), - // Copy static assets from `public` folder to `build` folder - new CopyWebpackPlugin([ - { - from: '**/*', - context: 'public', - }, - ]), - // Extract CSS into separate files - new MiniCssExtractPlugin({ - filename: '[name].css', - }), - ], -}; + }; -module.exports = common; + if(crossBrowser) { + // Copy browser-polyfill.js for cross browser compability + common.plugins.unshift(new CopyWebpackPlugin([ + { + from: 'node_modules/webextension-polyfill/dist/browser-polyfill.min.js' + } + ])); + } + return common; +} diff --git a/index.js b/index.js index d349726..22799fb 100755 --- a/index.js +++ b/index.js @@ -12,6 +12,7 @@ const packageFile = require('./package.json'); const { checkAppName, prettifyAppName } = require('./utils/name'); const generateReadme = require('./scripts/readme'); const tryGitInit = require('./scripts/git-init'); +const browserPolyfillFilename = 'browser-polyfill.min.js'; let projectName; const OVERRIDE_PAGES = ['newtab', 'bookmarks', 'history']; @@ -28,6 +29,7 @@ const program = new commander.Command(packageFile.name) 'override default page like New Tab, Bookmarks, or History page' ) .option('--devtools', 'add features to Chrome Developer Tools') + .option('--no-cross-browser', `the generated project will not be cross-browser compatible`) .on('--help', () => { console.log(` Only ${chalk.green('')} is required.`); }) @@ -90,7 +92,7 @@ function logOptionsConflictError() { process.exit(1); } -function createExtension(name, { overridePage, devtools }) { +function createExtension(name, { overridePage, devtools, crossBrowser }) { const root = path.resolve(name); let overridePageName; @@ -126,8 +128,8 @@ function createExtension(name, { overridePage, devtools }) { appPackage.scripts = { watch: - 'webpack --mode=development --watch --config config/webpack.config.js', - build: 'webpack --mode=production --config config/webpack.config.js', + `webpack --mode=development --env.CROSS_BROWSER=${crossBrowser} --watch --config config/webpack.config.js`, + build: `webpack --mode=production --env.CROSS_BROWSER=${crossBrowser} --config config/webpack.config.js`, }; // Create package file in project directory @@ -148,7 +150,8 @@ function createExtension(name, { overridePage, devtools }) { 'size-plugin', 'mini-css-extract-plugin', 'css-loader', - 'file-loader' + 'file-loader', + 'webextension-polyfill' ); console.log('Installing packages. This might take a couple of minutes.'); @@ -176,7 +179,36 @@ function createExtension(name, { overridePage, devtools }) { templateName = 'popup'; } - fs.copySync(path.resolve(__dirname, 'templates', templateName), root); + const files = []; + fs.copySync(path.resolve(__dirname, 'templates', templateName), root, { + filter: filename => { + const endsWithNoCrossbrowser = /\.nocrossbrowser\.js$/.test(filename); // determine if filename ends with '.nocrossbrowser.js' + if(!/\.js$/.test(filename)) return true; // copy all files that aren't .js files + if(/webpack\.config\.js$/.test(filename)) return true; // explcitly copy webpack.config.js all the time + if(!crossBrowser && endsWithNoCrossbrowser) { + files.push(filename); + return true; + } else if(crossBrowser && !endsWithNoCrossbrowser) { + return true; + } else if(!crossBrowser && !endsWithNoCrossbrowser) { + const parsedPath = path.parse(filename); + const pathWithCrossbrowser = path.resolve(parsedPath.dir, parsedPath.name, '.nocrossbrowser', parsedPath.ext); + return !fs.pathExists(pathWithCrossbrowser); + } + return false; + } + }); + + // if the webextenstion support is not enabled, we need to copy the template files with .nocrosbbrowser.js ending + // but after that, these files are renamed and the .nocrosbbrowser extension is removed + if(!crossBrowser) { + files.forEach(file => { + const { base } = path.parse(file); + let srcPath = path.resolve(root, 'src', base); + let destPath = path.resolve(root, 'src', base.replace('.nocrossbrowser', '')); + fs.moveSync(srcPath, destPath); + }); + } // Copy common webpack configuration file fs.copySync(path.resolve(__dirname, 'config'), path.join(root, 'config')); @@ -241,6 +273,13 @@ function createExtension(name, { overridePage, devtools }) { }; } + if(crossBrowser) { + appManifest.background.scripts = [browserPolyfillFilename, ...appManifest.background.scripts]; + if(appManifest.content_scripts) { + appManifest.content_scripts.forEach(i => i.js.unshift(browserPolyfillFilename)); + } + } + // Create manifest file in project directory fs.writeFileSync( path.join(root, 'public', 'manifest.json'), @@ -284,4 +323,5 @@ function createExtension(name, { overridePage, devtools }) { createExtension(projectName, { overridePage: program.overridePage, devtools: program.devtools, + crossBrowser: program.crossBrowser }); diff --git a/package-lock.json b/package-lock.json index 10a84a8..32b9e70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-cli", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -138,6 +138,12 @@ "builtins": "^1.0.3" } }, + "webextension-polyfill": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/webextension-polyfill/-/webextension-polyfill-0.5.0.tgz", + "integrity": "sha512-aFrl38x43t1bTboX/paCT8I97+idzX/TY0+fuM52hrIkCpYfROEF9kSn0BXuEIi3J9LTYt2ZZKkhx9NB1qF3nA==", + "dev": true + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index cea496e..89b7618 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "validate-npm-package-name": "^3.0.0" }, "devDependencies": { - "prettier": "^1.18.2" + "prettier": "^1.18.2", + "webextension-polyfill": "^0.5.0" } } diff --git a/templates/devtools/config/webpack.config.js b/templates/devtools/config/webpack.config.js index 158346f..ef2d025 100644 --- a/templates/devtools/config/webpack.config.js +++ b/templates/devtools/config/webpack.config.js @@ -2,16 +2,16 @@ const merge = require('webpack-merge'); -const common = require('./webpack.common.js'); +const getCommonConfig = require('./webpack.common.js'); const PATHS = require('./paths'); -// Merge webpack configuration files -const config = merge(common, { - entry: { - devtools: PATHS.src + '/devtools.js', - panel: PATHS.src + '/panel.js', - background: PATHS.src + '/background.js', - }, -}); - -module.exports = config; +module.exports = env => { + // Merge webpack configuration files + return merge(getCommonConfig(env), { + entry: { + devtools: PATHS.src + '/devtools.js', + panel: PATHS.src + '/panel.js', + background: PATHS.src + '/background.js', + }, + }); +} diff --git a/templates/devtools/public/devtools.html b/templates/devtools/public/devtools.html index 02c9464..aaaaad8 100644 --- a/templates/devtools/public/devtools.html +++ b/templates/devtools/public/devtools.html @@ -5,6 +5,7 @@ Chrome DevTools + <%= browser-polyfill %>