Skip to content

Commit

Permalink
feat: add typescript-webpack template (#1344)
Browse files Browse the repository at this point in the history
  • Loading branch information
codebytere authored and MarshallOfSound committed Dec 14, 2019
1 parent f1412e5 commit 7c8259d
Show file tree
Hide file tree
Showing 12 changed files with 354 additions and 4 deletions.
1 change: 1 addition & 0 deletions packages/api/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@electron-forge/shared-types": "6.0.0-beta.46",
"@electron-forge/template-webpack": "6.0.0-beta.46",
"@electron-forge/template-typescript": "6.0.0-beta.46",
"@electron-forge/template-typescript-webpack": "6.0.0-beta.46",
"@electron/get": "^1.6.0",
"colors": "^1.4.0",
"cross-spawn-promise": "^0.10.1",
Expand Down
52 changes: 48 additions & 4 deletions packages/api/core/test/slow/api_spec_slow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,16 +179,60 @@ describe(`electron-forge API (with installer=${nodeInstaller})`, () => {
const expectedFiles = [
'tsconfig.json',
'tslint.json',
path.join('src', 'index.ts'),
];

for (const filename of expectedFiles) {
await expectProjectPathExists(filename, 'file');
}
});

it('should convert the main process file to typescript', async () => {
await expectProjectPathNotExists(path.join('src', 'index.js'), 'file');
await expectProjectPathExists(path.join('src', 'index.ts'), 'file');
expect((await fs.readFile(path.join(dir, 'src', 'index.ts'))).toString()).to.match(/Electron.BrowserWindow/);
describe('lint', () => {
it('should initially pass the linting process', async () => {
try {
await forge.lint({ dir });
} catch (err) {
if (err.stdout) {
// eslint-disable-next-line no-console
console.error('STDOUT:', err.stdout.toString());
// eslint-disable-next-line no-console
console.error('STDERR:', err.stderr.toString());
}
throw err;
}
});
});

after(async () => {
await fs.remove(dir);
});
});

describe('init (with typescript-webpack templater)', () => {
before(ensureTestDirIsNonexistent);

it('should succeed in initializing the typescript template', async () => {
await forge.init({
dir,
template: 'typescript-webpack',
});
});

it('should copy the appropriate template files', async () => {
const expectedFiles = [
'tsconfig.json',
'tslint.json',
'webpack.main.config.js',
'webpack.renderer.config.js',
'webpack.rules.js',
'webpack.plugins.js',
path.join('src', 'index.ts'),
path.join('src', 'renderer.ts'),
];

for (const filename of expectedFiles) {
await expectProjectPathExists(filename, 'file');
}
});

describe('lint', () => {
Expand Down
21 changes: 21 additions & 0 deletions packages/template/typescript-webpack/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@electron-forge/template-typescript-webpack",
"version": "6.0.0-beta.46",
"description": "Typescript-Webpack template for Electron Forge",
"repository": "https://github.com/electron-userland/electron-forge",
"author": "Shelley Vohr <shelley.vohr@gmail.com>",
"license": "MIT",
"main": "dist/TypeScriptWebpackTemplate.js",
"typings": "dist/TypeScriptWebpackTemplate.d.ts",
"scripts": {
"test": "echo No Tests"
},
"engines": {
"node": ">= 8.0"
},
"dependencies": {
"@electron-forge/async-ora": "6.0.0-beta.46",
"@electron-forge/shared-types": "6.0.0-beta.46",
"fs-extra": "^8.1.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { ForgeTemplate } from '@electron-forge/shared-types';
import { asyncOra } from '@electron-forge/async-ora';

import fs from 'fs-extra';
import path from 'path';

const currentVersion = require('../package').version;

const copyTemplateFile = async (destDir: string, basename: string) => {
const templateDir = path.resolve(__dirname, '..', 'tmpl');
await fs.copy(path.join(templateDir, basename), path.resolve(destDir, basename));
};

const updateFileByLine = async (
inputPath: string,
lineHandler: (line: string) => string,
outputPath: string | undefined = undefined,
) => {
const fileContents = (await fs.readFile(inputPath, 'utf8')).split('\n').map(lineHandler).join('\n');
await fs.writeFile(outputPath || inputPath, fileContents);
if (outputPath !== undefined) {
await fs.remove(inputPath);
}
};

class TypeScriptWebpackTemplate implements ForgeTemplate {
public devDependencies = [
`@electron-forge/plugin-webpack@${currentVersion}`,
'@marshallofsound/webpack-asset-relocator-loader@^0.5.0',
'css-loader@^3.0.0',
'node-loader@^0.6.0',
'ts-loader@^6.2.1',
'style-loader@^0.23.1',
'typescript@^3.7.0',
'tslint@^5.20.0',
'fork-ts-checker-webpack-plugin@^3.1.1',
];

public initializeTemplate = async (directory: string) => {
await asyncOra('Setting up Forge configuration', async () => {
const packageJSONPath = path.resolve(directory, 'package.json');
const packageJSON = await fs.readJson(packageJSONPath);

packageJSON.main = '.webpack/main';
packageJSON.config.forge.plugins = packageJSON.config.forge.plugins || [];
packageJSON.config.forge.plugins.push([
'@electron-forge/plugin-webpack',
{
mainConfig: './webpack.main.config.js',
renderer: {
config: './webpack.renderer.config.js',
entryPoints: [{
html: './src/index.html',
js: './src/renderer.ts',
name: 'main_window',
}],
},
},
]);

// Configure scripts for TS template
packageJSON.scripts.lint = 'tslint -c tslint.json -p tsconfig.json';

await fs.writeJson(packageJSONPath, packageJSON, { spaces: 2 });
});

await asyncOra('Setting up TypeScript configuration', async () => {
const filePath = (fileName: string) => path.join(directory, 'src', fileName);

// Copy Webpack files
await copyTemplateFile(directory, 'webpack.main.config.js');
await copyTemplateFile(directory, 'webpack.renderer.config.js');
await copyTemplateFile(directory, 'webpack.rules.js');
await copyTemplateFile(directory, 'webpack.plugins.js');

await updateFileByLine(path.resolve(directory, 'src', 'index.html'), (line) => {
if (line.includes('link rel="stylesheet"')) return '';
return line;
});

// Copy tsconfig with a small set of presets
await copyTemplateFile(directory, 'tsconfig.json');

// Copy tslint config with recommended settings
await copyTemplateFile(directory, 'tslint.json');

// Remove index.js and replace with index.ts
await fs.remove(filePath('index.js'));
await copyTemplateFile(path.join(directory, 'src'), 'index.ts');

await copyTemplateFile(path.join(directory, 'src'), 'renderer.ts');
});
}
}

export default new TypeScriptWebpackTemplate();
58 changes: 58 additions & 0 deletions packages/template/typescript-webpack/tmpl/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { app, BrowserWindow } from 'electron';
declare const MAIN_WINDOW_WEBPACK_ENTRY: any;

// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
app.quit();
}

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow: Electron.BrowserWindow;

const createWindow = () => {
// Create the browser window.
mainWindow = new BrowserWindow({
height: 600,
width: 800,
});

// and load the index.html of the app.
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);

// Open the DevTools.
mainWindow.webContents.openDevTools();

// Emitted when the window is closed.
mainWindow.on('closed', () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
};

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);

// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
}
});

app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow();
}
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.
31 changes: 31 additions & 0 deletions packages/template/typescript-webpack/tmpl/renderer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* This file will automatically be loaded by webpack and run in the "renderer" context.
* To learn more about the differences between the "main" and the "renderer" context in
* Electron, visit:
*
* https://electronjs.org/docs/tutorial/application-architecture#main-and-renderer-processes
*
* By default, Node.js integration in this file is disabled. When enabling Node.js integration
* in a renderer process, please be aware of potential security implications. You can read
* more about security risks here:
*
* https://electronjs.org/docs/tutorial/security
*
* To enable Node.js integration in this file, open up `main.js` and enable the `nodeIntegration`
* flag:
*
* ```
* // Create the browser window.
* mainWindow = new BrowserWindow({
* width: 800,
* height: 600,
* webPreferences: {
* nodeIntegration: true
* }
* });
* ```
*/

import './index.css';

console.log('👋 This message is being logged by "renderer.js", included via webpack');
20 changes: 20 additions & 0 deletions packages/template/typescript-webpack/tmpl/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"allowJs": true,
"module": "commonjs",
"skipLibCheck": true,
"esModuleInterop": true,
"noImplicitAny": true,
"sourceMap": true,
"baseUrl": ".",
"outDir": "dist",
"moduleResolution": "node",
"resolveJsonModule": true,
"paths": {
"*": ["node_modules/*"]
}
},
"include": [
"src/**/*"
]
}
14 changes: 14 additions & 0 deletions packages/template/typescript-webpack/tmpl/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"defaultSeverity": "error",
"extends": [
"tslint:latest"
],
"jsRules": {},
"rules": {
"no-implicit-dependencies": [true, "dev"],
"no-var-requires": false,
"quotemark": [true, "single"],
"no-console": false
},
"rulesDirectory": []
}
14 changes: 14 additions & 0 deletions packages/template/typescript-webpack/tmpl/webpack.main.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
/**
* This is the main entry point for your application, it's the first file
* that runs in the main process.
*/
entry: './src/index.ts',
// Put your normal webpack config below here
module: {
rules: require('./webpack.rules'),
},
resolve: {
extensions: ['.js', '.ts', '.jsx', '.tsx', '.css', '.json']
},
};
7 changes: 7 additions & 0 deletions packages/template/typescript-webpack/tmpl/webpack.plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

module.exports = [
new ForkTsCheckerWebpackPlugin({
async: false
})
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const rules = require('./webpack.rules');
const plugins = require('./webpack.plugins');

rules.push({
test: /\.css$/,
use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
});

module.exports = {
module: {
rules,
},
plugins: plugins,
resolve: {
extensions: ['.js', '.ts', '.jsx', '.tsx', '.css']
},
};
27 changes: 27 additions & 0 deletions packages/template/typescript-webpack/tmpl/webpack.rules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module.exports = [
// Add support for native node modules
{
test: /\.node$/,
use: 'node-loader',
},
{
test: /\.(m?js|node)$/,
parser: { amd: false },
use: {
loader: '@marshallofsound/webpack-asset-relocator-loader',
options: {
outputAssetBase: 'native_modules',
},
},
},
{
test: /\.tsx?$/,
exclude: /(node_modules|.webpack)/,
loaders: [{
loader: 'ts-loader',
options: {
transpileOnly: true
}
}]
},
];

0 comments on commit 7c8259d

Please sign in to comment.