Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TypeScript support #21

Merged
merged 2 commits into from
Apr 17, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -171,6 +171,17 @@ chrome-extension-cli my-extension --override-page=history // Override Histo

Creates a Panel inside developer tools.

#### `chrome-extension-cli my-extension --language`

Creates an extension for supported languages like JavaScript and TypeScript.<br>
By default extension is created for JavaScript language and you can also pass other value to `--language` option to create extension for TypeScript.

```
chrome-extension-cli my-extension // Language JavaScript (default)
chrome-extension-cli my-extension --language=javascript // Language JavaScript
chrome-extension-cli my-extension --language=typescript // Language TypeScript
```

## Contributing

See the [contribution guide](CONTRIBUTING.md) and join the contributors!
File renamed without changes.
File renamed without changes.
75 changes: 75 additions & 0 deletions config/typescript/webpack.common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use strict';

const SizePlugin = require('size-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
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',
},
stats: {
all: false,
errors: true,
builtAt: true,
},
module: {
rules: [
// Check for TypeScript files
{
test: /\.ts$/,
use: ['ts-loader'],
},
// 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]',
},
},
],
},
],
},
resolve: {
// Help webpack resolve these extensions in order
extensions: ['.ts', '.js'],
},
plugins: [
// Print file sizes
new SizePlugin(),
// Copy static assets from `public` folder to `build` folder
new CopyWebpackPlugin({
patterns: [
{
from: '**/*',
context: 'public',
},
],
}),
// Extract CSS into separate files
new MiniCssExtractPlugin({
filename: '[name].css',
}),
],
};

module.exports = common;
89 changes: 86 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -14,7 +14,17 @@ const generateReadme = require('./scripts/readme');
const tryGitInit = require('./scripts/git-init');

let projectName;
const LANGUAGES = ['javascript', 'typescript'];
const OVERRIDE_PAGES = ['newtab', 'bookmarks', 'history'];
const TS_CONFIG = {
compilerOptions: {
rootDir: 'src',
target: 'es6',
module: 'commonjs',
esModuleInterop: true,
strict: true,
},
};

const program = new commander.Command(packageFile.name)
.version(packageFile.version)
@@ -28,6 +38,10 @@ 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(
'--language [language-name]',
'language like JavaScript and TypeScript'
)
.on('--help', () => {
console.log(` Only ${chalk.green('<project-directory>')} is required.`);
})
@@ -57,6 +71,14 @@ function isOverridePageNameValid(name) {
return false;
}

function isLanguageNameValid(name) {
if (name === true || LANGUAGES.includes(name)) {
return true;
}

return false;
}

function logOverridePageError() {
console.error(
`${chalk.red('Invalid page name passed to option:')} ${chalk.cyan(
@@ -79,6 +101,28 @@ function logOverridePageError() {
process.exit(1);
}

function logLanguageError() {
console.error(
`${chalk.red('Invalid language name passed to option:')} ${chalk.cyan(
'--language'
)}`
);
console.log();
console.log(
`You can pass language name as ${chalk.cyan('javascript')} or ${chalk.cyan(
'typescript'
)}.`
);
console.log();
console.log('For example:');
console.log(
` ${chalk.cyan(program.name())} ${chalk.green(
'my-extension'
)} ${chalk.cyan('--language')} ${chalk.green('typescript')}`
);
process.exit(1);
}

function logOptionsConflictError() {
console.error(
`${chalk.red(
@@ -90,9 +134,10 @@ function logOptionsConflictError() {
process.exit(1);
}

function createExtension(name, { overridePage, devtools }) {
function createExtension(name, { overridePage, devtools, language }) {
const root = path.resolve(name);
let overridePageName;
let languageName = 'javascript';

if (overridePage) {
if (isOverridePageNameValid(overridePage)) {
@@ -106,6 +151,14 @@ function createExtension(name, { overridePage, devtools }) {
}
}

if (language) {
if (isLanguageNameValid(language)) {
languageName = language === true ? 'javascript' : language;
} else {
logLanguageError();
}
}

checkAppName(name);
fs.ensureDirSync(name);

@@ -141,6 +194,14 @@ function createExtension(name, { overridePage, devtools }) {
JSON.stringify(appPackage, null, 2)
);

// Create tsconfig file in project directory
if (languageName === 'typescript') {
fs.writeFileSync(
path.join(root, 'tsconfig.json'),
JSON.stringify(TS_CONFIG, null, 2)
);
}

let command = 'npm';
let args = ['install', '--save-dev'];

@@ -156,6 +217,10 @@ function createExtension(name, { overridePage, devtools }) {
'file-loader@^6.0.0'
);

if (languageName === 'typescript') {
args.push('typescript@4.6.3', 'ts-loader@8.3.0', '@types/chrome@0.0.181');
}

console.log('Installing packages. This might take a couple of minutes.');
console.log(
`Installing ${chalk.cyan('webpack')}, ${chalk.cyan(
@@ -181,10 +246,27 @@ function createExtension(name, { overridePage, devtools }) {
templateName = 'popup';
}

fs.copySync(path.resolve(__dirname, 'templates', templateName), root);
fs.copySync(
path.resolve(__dirname, 'templates', languageName, templateName),
root
);

// Copy common files between languages
fs.copySync(
path.resolve(__dirname, 'templates', 'shared', templateName),
root
);

fs.copySync(
path.resolve(__dirname, 'config', languageName),
path.join(root, 'config')
);

// Copy common webpack configuration file
fs.copySync(path.resolve(__dirname, 'config'), path.join(root, 'config'));
fs.copySync(
path.resolve(__dirname, 'config', 'shared'),
path.join(root, 'config')
);

// Rename gitignore after the fact to prevent npm from renaming it to .npmignore
// See: https://github.com/npm/npm/issues/1862
@@ -289,4 +371,5 @@ function createExtension(name, { overridePage, devtools }) {
createExtension(projectName, {
overridePage: program.overridePage,
devtools: program.devtools,
language: program.language,
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
"chrome-extension-cli": "index.js"
},
"scripts": {
"format": "prettier --single-quote --trailing-comma es5 --write 'index.js' '{config,scripts,utils,templates}/**/*.{js,css}'"
"format": "prettier --single-quote --trailing-comma es5 --write 'index.js' '{config,scripts,utils,templates}/**/*.{js,ts,css}'"
},
"keywords": [
"chrome",
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes.
8 changes: 8 additions & 0 deletions templates/typescript/chrome.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// A temporary solution to ignore TypeScript errors
declare var chrome: {
runtime: any;
panel: any;
devtools: any;
storage: any;
tabs: any;
};
19 changes: 19 additions & 0 deletions templates/typescript/devtools/config/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict';

const { merge } = require('webpack-merge');

const common = require('./webpack.common.js');
const PATHS = require('./paths');

// Merge webpack configuration files
const config = (env, argv) =>
merge(common, {
entry: {
devtools: PATHS.src + '/devtools.ts',
panel: PATHS.src + '/panel.ts',
background: PATHS.src + '/background.ts',
},
devtool: argv.mode === 'production' ? false : 'source-map',
});

module.exports = config;
19 changes: 19 additions & 0 deletions templates/typescript/devtools/src/background.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict';

// With background scripts you can communicate with popup
// and contentScript files.
// For more information on background script,
// See https://developer.chrome.com/extensions/background_pages

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === 'GREETINGS') {
const message: string = `Hi Pan, my name is Bac. I am from Background. It's great to hear from you.`;

// Log message coming from the `request` parameter
console.log(request.payload.message);
// Send a response message
sendResponse({
message,
});
}
});
10 changes: 10 additions & 0 deletions templates/typescript/devtools/src/devtools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';

// A DevTools extension adds functionality to the Chrome DevTools.
// For more information on DevTools,
// See https://developer.chrome.com/extensions/devtools

// Create a panel named `My Panel`
chrome.devtools.panels.create('My Panel', '', 'panel.html', panel => {
console.log('Panel was successfully created!');
});
55 changes: 55 additions & 0 deletions templates/typescript/devtools/src/panel.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* normalize css starts here */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* normalize css ends here */

html {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
sans-serif;
color: #222;
}

body {
width: 100vw;
height: 100vh;
background-color: #fff;
}

.app {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
text-align: center;
padding: 20px;
}

code {
font-size: 12px;
font-family: inherit;
background-color: rgba(254, 237, 185, 0.3);
padding: 2px 4px;
border-radius: 2px;
}

.title {
font-size: 18px;
margin-bottom: 10px;
}

.message {
font-size: 22px;
}

.divider {
margin: 30px auto;
width: 50px;
border: 0.5px dashed #000;
opacity: 0.2;
}
Loading