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

feat(template): add vite-typescript template #3178

Merged
merged 24 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
82309e2
feat(template): add vite-typescript template
caoxiemeihao Mar 3, 2023
e9cfca2
faet: add test for vite-typescript template
caoxiemeihao Mar 4, 2023
48dfa56
refactor(template-vite-typescript): `@electron-forge/plugin-vite` ins…
caoxiemeihao Apr 1, 2023
7166887
feat(template-vite-typescript): move declare to .d.ts
caoxiemeihao Apr 12, 2023
f4b23b9
chore(template-vite-typescript): update tsconfig
caoxiemeihao Apr 12, 2023
488aa0a
chore(template-vite-typescript): bump `@electron-forge/*` to 6.1.1
caoxiemeihao Apr 12, 2023
a7198dd
fix(template-vite-typescript): CI test
caoxiemeihao Apr 23, 2023
e2e74a6
fix(template-vite-typescript): correct resolve lib as Node.js env
caoxiemeihao Apr 23, 2023
539518a
fix(template-vite-typescript): update `scripts.lint`
caoxiemeihao Apr 23, 2023
4f1b7fd
fix(template-vite-typescript): CI Windows
caoxiemeihao Apr 25, 2023
06957e9
chore(template-vite-typescript): bump electron relatived deps to 6.2.1
caoxiemeihao Jul 4, 2023
0f4ec09
fix(template-vite-typescript): commonjs instead ESNext
caoxiemeihao Jul 4, 2023
d94fc8b
cleanup: code style
caoxiemeihao Aug 13, 2023
96e1924
fix: use fs sync api bypass Windows test BUG
caoxiemeihao Aug 13, 2023
a61c588
fix: kill esbuild for test vite-typescript tpl
caoxiemeihao Aug 13, 2023
d8d8ef1
Update packages/template/vite-typescript/test/ViteTypeScriptTemplate_…
caoxiemeihao Aug 14, 2023
762d6e8
Update packages/template/vite-typescript/test/ViteTypeScriptTemplate_…
caoxiemeihao Aug 14, 2023
70f2245
Update packages/template/vite-typescript/test/ViteTypeScriptTemplate_…
caoxiemeihao Aug 14, 2023
a5d04fd
Update packages/template/vite-typescript/test/ViteTypeScriptTemplate_…
caoxiemeihao Aug 14, 2023
8e17895
Update packages/template/vite-typescript/test/ViteTypeScriptTemplate_…
caoxiemeihao Aug 14, 2023
0b1ffd2
fix: eslint
caoxiemeihao Aug 14, 2023
4f1aa6e
chore(vite-typescript): bump to 6.3.0
caoxiemeihao Aug 14, 2023
3c0de1f
Merge branch 'main' of https://github.com/electron/forge into vite-ty…
caoxiemeihao Aug 14, 2023
a1c1688
chore(vite-typescript): add files field
caoxiemeihao Aug 16, 2023
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
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
"lint:fix": "prettier --write .",
"link:prepare": "lerna exec -- node ../../../tools/silent.js yarn link --silent --no-bin-links --link-folder ../../../.links",
"link:remove": "lerna exec -- node ../../../tools/silent.js yarn unlink --silent --no-bin-links --link-folder ../../../.links",
"test": "xvfb-maybe cross-env LINK_FORGE_DEPENDENCIES_ON_INIT=1 TS_NODE_PROJECT='./tsconfig.test.json' TS_NODE_FILES=1 mocha './tools/test-globber.ts'",
"test:fast": "xvfb-maybe cross-env LINK_FORGE_DEPENDENCIES_ON_INIT=1 TS_NODE_PROJECT='./tsconfig.test.json' TEST_FAST_ONLY=1 TS_NODE_FILES=1 mocha './tools/test-globber.ts'",
"test:slow": "xvfb-maybe cross-env LINK_FORGE_DEPENDENCIES_ON_INIT=1 TS_NODE_PROJECT='./tsconfig.test.json' TEST_SLOW_ONLY=1 TS_NODE_FILES=1 mocha './tools/test-globber.ts'",
"test": "xvfb-maybe cross-env NODE_ENV=test LINK_FORGE_DEPENDENCIES_ON_INIT=1 TS_NODE_PROJECT='./tsconfig.test.json' TS_NODE_FILES=1 mocha './tools/test-globber.ts'",
"test:fast": "xvfb-maybe cross-env NODE_ENV=test LINK_FORGE_DEPENDENCIES_ON_INIT=1 TS_NODE_PROJECT='./tsconfig.test.json' TEST_FAST_ONLY=1 TS_NODE_FILES=1 mocha './tools/test-globber.ts'",
"test:slow": "xvfb-maybe cross-env NODE_ENV=test LINK_FORGE_DEPENDENCIES_ON_INIT=1 TS_NODE_PROJECT='./tsconfig.test.json' TEST_SLOW_ONLY=1 TS_NODE_FILES=1 mocha './tools/test-globber.ts'",
"postinstall": "rimraf node_modules/.bin/*.ps1 && ts-node ./tools/gen-tsconfigs.ts && ts-node ./tools/gen-ts-glue.ts",
"prepare": "husky install",
"preversion": "yarn build"
Expand Down
1 change: 1 addition & 0 deletions packages/template/vite-typescript/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tmpl
36 changes: 36 additions & 0 deletions packages/template/vite-typescript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@electron-forge/template-vite-typescript",
"version": "6.3.0",
"description": "Vite-TypeScript template for Electron Forge, gets you started with Vite really quickly",
"repository": {
"type": "git",
"url": "https://github.com/electron/forge",
"directory": "packages/template/vite-typescript"
},
"author": "caoxiemeihao",
"license": "MIT",
"main": "dist/ViteTypeScriptTemplate.js",
dsanders11 marked this conversation as resolved.
Show resolved Hide resolved
"typings": "dist/ViteTypeScriptTemplate.d.ts",
"scripts": {
"test": "mocha --config ../../../.mocharc.js test/**/*_spec_slow.ts"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"dependencies": {
"@electron-forge/shared-types": "6.3.0",
"@electron-forge/template-base": "6.3.0",
"fs-extra": "^10.0.0"
},
"devDependencies": {
"@electron-forge/core-utils": "6.3.0",
"@electron-forge/test-utils": "6.3.0",
"chai": "^4.3.3",
"fast-glob": "^3.2.7"
},
"files": [
"dist",
"src",
"tmpl"
]
}
72 changes: 72 additions & 0 deletions packages/template/vite-typescript/src/ViteTypeScriptTemplate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import path from 'path';

import { ForgeListrTaskDefinition, InitTemplateOptions } from '@electron-forge/shared-types';
import { BaseTemplate } from '@electron-forge/template-base';
import fs from 'fs-extra';

class ViteTypeScriptTemplate extends BaseTemplate {
public templateDir = path.resolve(__dirname, '..', 'tmpl');

public async initializeTemplate(directory: string, options: InitTemplateOptions): Promise<ForgeListrTaskDefinition[]> {
const superTasks = await super.initializeTemplate(directory, options);
return [
...superTasks,
{
title: 'Setting up Forge configuration',
task: async () => {
await this.copyTemplateFile(directory, 'forge.config.ts');
await fs.remove(path.resolve(directory, 'forge.config.js'));
},
},
{
title: 'Preparing TypeScript files and configuration',
task: async () => {
const filePath = (fileName: string) => path.join(directory, 'src', fileName);

// Copy Vite files
await this.copyTemplateFile(directory, 'vite.main.config.ts');
await this.copyTemplateFile(directory, 'vite.renderer.config.ts');
await this.copyTemplateFile(directory, 'vite.preload.config.ts');

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

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

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

await this.copyTemplateFile(path.join(directory, 'src'), 'renderer.ts');
await this.copyTemplateFile(path.join(directory, 'src'), 'types.d.ts');

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

// TODO: Compatible with any path entry.
// Vite uses index.html under the root path as the entry point.
await fs.move(filePath('index.html'), path.join(directory, 'index.html'));
await this.updateFileByLine(path.join(directory, 'index.html'), (line) => {
if (line.includes('link rel="stylesheet"')) return '';
if (line.includes('</body>')) return ' <script type="module" src="/src/renderer.ts"></script>\n </body>';
return line;
});

// update package.json
const packageJSONPath = path.resolve(directory, 'package.json');
const packageJSON = await fs.readJson(packageJSONPath);
packageJSON.main = '.vite/build/main.js';
// Configure scripts for TS template
packageJSON.scripts.lint = 'eslint --ext .ts,.tsx .';
await fs.writeJson(packageJSONPath, packageJSON, {
spaces: 2,
});
},
},
];
}
}

export default new ViteTypeScriptTemplate();
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import cp from 'child_process';
import path from 'path';

import { yarnOrNpmSpawn } from '@electron-forge/core-utils';
import * as testUtils from '@electron-forge/test-utils';
import { expect } from 'chai';
import glob from 'fast-glob';
import fs from 'fs-extra';

import { api } from '../../../api/core';
import { initLink } from '../../../api/core/src/api/init-scripts/init-link';

describe('ViteTypeScriptTemplate', () => {
let dir: string;

before(async () => {
await yarnOrNpmSpawn(['link:prepare']);
dir = await testUtils.ensureTestDirIsNonexistent();
});

after(async () => {
await yarnOrNpmSpawn(['link:remove']);
await killWindowsEsbuildExe();
await fs.remove(dir);
});

describe('template files are copied to project', () => {
it('should succeed in initializing the typescript template', async () => {
await api.init({
dir,
template: path.resolve(__dirname, '..', 'src', 'ViteTypeScriptTemplate'),
interactive: false,
});
});

const expectedFiles = [
'tsconfig.json',
'.eslintrc.json',
'forge.config.ts',
'vite.main.config.ts',
'vite.renderer.config.ts',
'vite.preload.config.ts',
path.join('src', 'main.ts'),
path.join('src', 'renderer.ts'),
path.join('src', 'preload.ts'),
path.join('src', 'types.d.ts'),
];
for (const filename of expectedFiles) {
it(`${filename} should exist`, async () => {
await testUtils.expectProjectPathExists(dir, filename, 'file');
});
}

it('should ensure js source files from base template are removed', async () => {
const jsFiles = await glob(path.join(dir, 'src', '**', '*.js'));
expect(jsFiles.length).to.equal(0, `The following unexpected js files were found in the src/ folder: ${JSON.stringify(jsFiles)}`);
});
});

describe('lint', () => {
it('should initially pass the linting process', async () => {
delete process.env.TS_NODE_PROJECT;
await testUtils.expectLintToPass(dir);
});
});

describe('package', () => {
let cwd: string;

before(async () => {
delete process.env.TS_NODE_PROJECT;
// Vite resolves plugins via cwd
cwd = process.cwd();
process.chdir(dir);
// We need the version of vite to match exactly during development due to a quirk in
// typescript type-resolution. In prod no one has to worry about things like this
const pj = await fs.readJson(path.resolve(dir, 'package.json'));
pj.resolutions = {
// eslint-disable-next-line @typescript-eslint/no-var-requires
vite: `${require('../../../../node_modules/vite/package.json').version}`,
};
await fs.writeJson(path.resolve(dir, 'package.json'), pj);
await yarnOrNpmSpawn(['install'], {
cwd: dir,
});

// Installing deps removes symlinks that were added at the start of this
// spec via `api.init`. So we should re-link local forge dependencies
// again.
await initLink(dir);
});

after(() => {
process.chdir(cwd);
});

it('should pass', async () => {
await api.package({
dir,
interactive: false,
});
});
});
});

/**
* TODO: resolve `esbuild` can not exit normally on the Windows platform.
* @deprecated
*/
async function killWindowsEsbuildExe() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BlackHole1 Until Vite fix it, we can resolve the problem like this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an open issue tracking this for Vite?

if (process.platform !== 'win32') {
return Promise.resolve();
}

return new Promise<void>((resolve, reject) => {
cp.exec('tasklist', (error, stdout) => {
if (error) {
reject(error);
return;
}

const esbuild = stdout
.toString()
.split('\n')
.map((line) => line.split(/\s+/))
.find((line) => line.includes('esbuild.exe'));

if (!esbuild) {
resolve();
return;
}

// ['esbuild.exe', '4564', 'Console', '1', '14,400', 'K', '']
const [, pid] = esbuild;
const result = process.kill(+pid, 'SIGINT');

if (result) {
resolve();
} else {
reject(new Error('kill esbuild process failed'));
}
});
});
}
16 changes: 16 additions & 0 deletions packages/template/vite-typescript/tmpl/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/electron",
"plugin:import/typescript"
],
"parser": "@typescript-eslint/parser"
}
37 changes: 37 additions & 0 deletions packages/template/vite-typescript/tmpl/forge.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { ForgeConfig } from '@electron-forge/shared-types';
import { MakerSquirrel } from '@electron-forge/maker-squirrel';
import { MakerZIP } from '@electron-forge/maker-zip';
import { MakerDeb } from '@electron-forge/maker-deb';
import { MakerRpm } from '@electron-forge/maker-rpm';
import { VitePlugin } from '@electron-forge/plugin-vite';

const config: ForgeConfig = {
packagerConfig: {},
rebuildConfig: {},
makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin']), new MakerRpm({}), new MakerDeb({})],
plugins: [
new VitePlugin({
// `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.
// If you are familiar with Vite configuration, it will look really familiar.
build: [
{
// `entry` is just an alias for `build.lib.entry` in the corresponding file of `config`.
entry: 'src/main.ts',
config: 'vite.main.config.ts',
},
{
entry: 'src/preload.ts',
config: 'vite.preload.config.ts',
},
],
renderer: [
{
name: 'main_window',
config: 'vite.renderer.config.ts',
},
],
}),
],
};

export default config;
53 changes: 53 additions & 0 deletions packages/template/vite-typescript/tmpl/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { app, BrowserWindow } from 'electron';
import path from 'path';

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

const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});

// and load the index.html of the app.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
} else {
mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
}

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

// 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, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
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 (BrowserWindow.getAllWindows().length === 0) {
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.
11 changes: 11 additions & 0 deletions packages/template/vite-typescript/tmpl/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"devDependencies": {
"@electron-forge/plugin-vite": "ELECTRON_FORGE/VERSION",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-plugin-import": "^2.25.0",
"ts-node": "^10.0.0",
"typescript": "~4.5.4"
}
}
2 changes: 2 additions & 0 deletions packages/template/vite-typescript/tmpl/preload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// See the Electron documentation for details on how to use preload scripts:
// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
Loading