diff --git a/.babelrc b/.babelrc index e5d69ac067..ce83cd594e 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,8 @@ { "sourceMaps": "inline", + "ignore": [ + "./tmpl" + ], "plugins": [ "@babel/plugin-proposal-class-properties", "source-map-support" diff --git a/.eslintignore b/.eslintignore index a25a816623..7b0fc151ae 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,3 +2,4 @@ dist node_modules *.d.ts packages/*/*/index.ts +tmpl diff --git a/.gitignore b/.gitignore index e28f676b7f..896e9e7cd7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,10 @@ yarn-error.log packages/.old **/tsconfig.json !/tsconfig.json +!packages/template/*/tmpl/tsconfig.json **/tslint.json !/tslint.json +!packages/template/*/tmpl/tslint.json docs doc .vscode diff --git a/packages/api/core/package.json b/packages/api/core/package.json index 2bbe412d6b..fdbe481444 100644 --- a/packages/api/core/package.json +++ b/packages/api/core/package.json @@ -38,6 +38,7 @@ "@electron-forge/publisher-base": "6.0.0-beta.46", "@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/get": "^1.6.0", "colors": "^1.4.0", "cross-spawn-promise": "^0.10.1", diff --git a/packages/api/core/test/slow/api_spec_slow.ts b/packages/api/core/test/slow/api_spec_slow.ts index 9bbb5abd18..43373c694d 100644 --- a/packages/api/core/test/slow/api_spec_slow.ts +++ b/packages/api/core/test/slow/api_spec_slow.ts @@ -109,10 +109,10 @@ describe(`electron-forge API (with installer=${nodeInstaller})`, () => { }); }); - describe('init (with built-in templater)', () => { + describe('init (with webpack templater)', () => { before(ensureTestDirIsNonexistent); - it('should succeed in initializing', async () => { + it('should succeed in initializing the webpack template', async () => { await forge.init({ dir, template: 'webpack', @@ -165,6 +165,37 @@ describe(`electron-forge API (with installer=${nodeInstaller})`, () => { }); }); + describe('init (with typescript templater)', () => { + before(ensureTestDirIsNonexistent); + + it('should succeed in initializing the typescript template', async () => { + await forge.init({ + dir, + template: 'typescript', + }); + }); + + it('should copy the appropriate template files', async () => { + const expectedFiles = [ + 'tsconfig.json', + 'tslint.json', + ]; + 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/); + }); + + after(async () => { + await fs.remove(dir); + }); + }); + describe('init (with a nonexistent templater)', () => { before(ensureTestDirIsNonexistent); diff --git a/packages/template/typescript/package.json b/packages/template/typescript/package.json new file mode 100644 index 0000000000..adfd7eca4d --- /dev/null +++ b/packages/template/typescript/package.json @@ -0,0 +1,21 @@ +{ + "name": "@electron-forge/template-typescript", + "version": "6.0.0-beta.46", + "description": "Typescript template for Electron Forge, gets you started with TypeScript really quickly", + "repository": "https://github.com/electron-userland/electron-forge", + "author": "Shelley Vohr ", + "license": "MIT", + "main": "dist/TypeScriptTemplate.js", + "typings": "dist/TypeScriptTemplate.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" + } +} diff --git a/packages/template/typescript/src/TypeScriptTemplate.ts b/packages/template/typescript/src/TypeScriptTemplate.ts new file mode 100644 index 0000000000..a556163620 --- /dev/null +++ b/packages/template/typescript/src/TypeScriptTemplate.ts @@ -0,0 +1,46 @@ +import { ForgeTemplate } from '@electron-forge/shared-types'; +import { asyncOra } from '@electron-forge/async-ora'; + +import fs from 'fs-extra'; +import path from 'path'; + +const copyTemplateFile = async (destDir: string, basename: string) => { + const templateDir = path.resolve(__dirname, '..', 'tmpl'); + await fs.copy(path.join(templateDir, basename), path.resolve(destDir, basename)); +}; + +class TypeScriptTemplate implements ForgeTemplate { + public devDependencies = [ + 'typescript@^3.7.0', + 'tslint@^5.20.0', + ]; + + 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); + + // Configure scripts for TS template + packageJSON.scripts.lint = 'tslint -c tslint.json -p tsconfig.json'; + packageJSON.scripts.start = 'tsc && electron-forge start -p dist'; + + await fs.writeJson(packageJSONPath, packageJSON, { spaces: 2 }); + }); + + await asyncOra('Setting up TypeScript configuration', async () => { + const filePath = (fileName: string) => path.join(directory, 'src', fileName); + + // 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'); + }); + } +} + +export default new TypeScriptTemplate(); diff --git a/packages/template/typescript/tmpl/index.ts b/packages/template/typescript/tmpl/index.ts new file mode 100644 index 0000000000..00f23c8fd6 --- /dev/null +++ b/packages/template/typescript/tmpl/index.ts @@ -0,0 +1,58 @@ +import { app, BrowserWindow } from 'electron'; +import * as path from 'path'; + +// 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({ + width: 800, + height: 600, + }); + + // and load the index.html of the app. + mainWindow.loadFile(path.join(__dirname, "../src/index.html")); + + // 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. diff --git a/packages/template/typescript/tmpl/tsconfig.json b/packages/template/typescript/tmpl/tsconfig.json new file mode 100644 index 0000000000..3cdb18c364 --- /dev/null +++ b/packages/template/typescript/tmpl/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "allowJs": true, + "module": "commonjs", + "noImplicitAny": true, + "sourceMap": true, + "baseUrl": ".", + "outDir": "dist", + "moduleResolution": "node", + "resolveJsonModule": true, + "paths": { + "*": ["node_modules/*"] + } + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/template/typescript/tmpl/tslint.json b/packages/template/typescript/tmpl/tslint.json new file mode 100644 index 0000000000..8b569d647b --- /dev/null +++ b/packages/template/typescript/tmpl/tslint.json @@ -0,0 +1,9 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:latest" + ], + "jsRules": {}, + "rules": {}, + "rulesDirectory": [] +} diff --git a/tsconfig.packages.json b/tsconfig.packages.json index 95180ae492..1d0f8492f2 100755 --- a/tsconfig.packages.json +++ b/tsconfig.packages.json @@ -3,13 +3,14 @@ "compilerOptions": { "rootDir": "packages" }, - "exclude": [ + "exclude": [ "dist", "node_modules", "ci", "tools", "**/dist", "**/node_modules", - "**/test", - ] -} \ No newline at end of file + "**/test", + "**/tmpl" + ] +}