diff --git a/.gitignore b/.gitignore index 879ec546b8..10ea1153d4 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ packages/plugin/**/README.md packages/api/cli/README.md !packages/**/base/README.md !/README.md +.webpack diff --git a/package.json b/package.json index a1086a4465..9dd209ec9a 100644 --- a/package.json +++ b/package.json @@ -24,16 +24,16 @@ "build:quick": "bolt ws exec -- node_modules/.bin/babel src -d dist --quiet --extensions \".ts\" --config-file ../../../.babelrc", "postbuild": "ts-node tools/test-dist", "commit": "git-cz", - "coverage:fast": "cross-env INTEGRATION_TESTS=0 TS_NODE_FILES=true nyc mocha './tools/test-globber.ts' && nyc report --reporter=text-lcov > coverage.lcov", - "coverage:slow": "cross-env TS_NODE_FILES=true nyc mocha './tools/test-globber.ts' --integration && nyc report --reporter=text-lcov > coverage.lcov", + "coverage:fast": "xvfb-maybe cross-env INTEGRATION_TESTS=0 TS_NODE_FILES=true nyc mocha './tools/test-globber.ts' && nyc report --reporter=text-lcov > coverage.lcov", + "coverage:slow": "xvfb-maybe cross-env TS_NODE_FILES=true nyc mocha './tools/test-globber.ts' --integration && nyc report --reporter=text-lcov > coverage.lcov", "docs": "bolt docs:generate && bolt docs:position", "docs:generate": "bolt ws exec -- node_modules/.bin/typedoc --out doc --excludeExternals --ignoreCompilerErrors --mode file --excludePrivate --excludeProtected --hideGenerator", "docs:position": "ts-node tools/position-docs.ts", "docs:deploy": "ts-node tools/sync-readmes.ts && bolt docs && ts-node tools/copy-now.ts && cd docs && now && now alias", "docs:deploy:ci": "ts-node tools/sync-readmes.ts && bolt docs && ts-node tools/copy-now.ts && cd docs && now --token $NOW_TOKEN && now alias --token $NOW_TOKEN", "lint": "eslint --ext .ts .", - "test": "cross-env TS_NODE_FILES=true yarn run mocha './tools/test-globber.ts'", - "test:fast": "cross-env TEST_FAST_ONLY=1 TS_NODE_FILES=true yarn run mocha './tools/test-globber.ts'", + "test": "xvfb-maybe cross-env TS_NODE_FILES=true mocha './tools/test-globber.ts'", + "test:fast": "xvfb-maybe cross-env TEST_FAST_ONLY=1 TS_NODE_FILES=true mocha './tools/test-globber.ts'", "postinstall": "rimraf node_modules/.bin/*.ps1 && ts-node tools/link-ts.ts" }, "dependencies": { @@ -143,7 +143,8 @@ "sinon-chai": "^3.6.0", "ts-node": "^10.0.0", "typedoc": "^0.21.0", - "typescript": "~4.0.2" + "typescript": "~4.0.2", + "xvfb-maybe": "^0.2.1" }, "optionalDependencies": { "@malept/electron-installer-flatpak": "^0.11.2", diff --git a/packages/plugin/webpack/package.json b/packages/plugin/webpack/package.json index 8feebaeada..ab815d5d4e 100644 --- a/packages/plugin/webpack/package.json +++ b/packages/plugin/webpack/package.json @@ -8,13 +8,15 @@ "main": "dist/WebpackPlugin.js", "typings": "dist/WebpackPlugin.d.ts", "scripts": { - "test": "mocha --config ../../../.mocharc.js test/**/*_spec.ts test/**/**/*_spec.ts" + "test": "xvfb-maybe mocha --config ../../../.mocharc.js test/**/*_spec.ts test/**/**/*_spec.ts" }, "devDependencies": { + "@malept/cross-spawn-promise": "^2.0.0", "@types/node": "^16.3.1", "chai": "^4.3.3", "mocha": "^9.0.1", - "sinon": "^11.1.1" + "sinon": "^11.1.1", + "xvfb-maybe": "^0.2.1" }, "engines": { "node": ">= 12.13.0" diff --git a/packages/plugin/webpack/src/WebpackConfig.ts b/packages/plugin/webpack/src/WebpackConfig.ts index 02908f6f4f..d795708dbe 100644 --- a/packages/plugin/webpack/src/WebpackConfig.ts +++ b/packages/plugin/webpack/src/WebpackConfig.ts @@ -4,6 +4,7 @@ import path from 'path'; import webpack, { Configuration, WebpackPluginInstance } from 'webpack'; import { merge as webpackMerge } from 'webpack-merge'; import { WebpackPluginConfig, WebpackPluginEntryPoint, WebpackPreloadEntryPoint } from './Config'; +import AssetRelocatorPatch from './util/AssetRelocatorPatch'; type EntryType = string | string[] | Record; @@ -88,24 +89,8 @@ export default class WebpackConfigGenerator { return 'undefined'; } - assetRelocatorBaseDir(inRendererDir = true) { - if (this.isProd) { - return `process.resourcesPath + "/" + (__filename.includes(".asar") ? "app.asar" : "app") + "/.webpack/${inRendererDir ? 'main' : 'renderer/any_folder'}"`; - } - - return JSON.stringify( - path.resolve( - this.webpackDir, - inRendererDir ? 'main' : 'renderer', - inRendererDir ? '.' : 'any_folder', - ), - ); - } - getDefines(inRendererDir = true) { - const defines: { [key: string]: string; } = { - ASSET_RELOCATOR_BASE_DIR: this.assetRelocatorBaseDir(inRendererDir), - }; + const defines: Record = {}; if ( !this.pluginConfig.renderer.entryPoints || !Array.isArray(this.pluginConfig.renderer.entryPoints) @@ -215,7 +200,9 @@ export default class WebpackConfigGenerator { template: entryPoint.html, filename: `${entryPoint.name}/index.html`, chunks: [entryPoint.name].concat(entryPoint.additionalChunks || []), - }) as WebpackPluginInstance).concat([new webpack.DefinePlugin(defines)]); + }) as WebpackPluginInstance).concat( + [new webpack.DefinePlugin(defines), new AssetRelocatorPatch(this.isProd)], + ); return webpackMerge({ entry, devtool: this.rendererSourceMapOption, diff --git a/packages/plugin/webpack/src/util/AssetRelocatorPatch.ts b/packages/plugin/webpack/src/util/AssetRelocatorPatch.ts new file mode 100644 index 0000000000..6d6b9093e0 --- /dev/null +++ b/packages/plugin/webpack/src/util/AssetRelocatorPatch.ts @@ -0,0 +1,59 @@ +import { Chunk, Compiler } from 'webpack'; + +export default class AssetRelocatorPatch { + private readonly isProd: boolean; + + constructor(isProd: boolean) { + this.isProd = isProd; + } + + public apply(compiler: Compiler) { + compiler.hooks.compilation.tap( + 'asset-relocator-forge-patch', + (compilation) => { + // We intercept the Vercel loader code injection and replace __dirname with + // code that works with Electron Forge + // + // Here is where the injection occurs: + // https://github.com/vercel/webpack-asset-relocator-loader/blob/4710a018fc8fb64ad51efb368759616fb273618f/src/asset-relocator.js#L331-L339 + compilation.mainTemplate.hooks.requireExtensions.intercept({ + register: (tapInfo) => { + if (tapInfo.name === 'asset-relocator-loader') { + const originalFn = tapInfo.fn as (source: string, chunk: Chunk) => string; + + tapInfo.fn = (source: string, chunk: Chunk) => { + const originalInjectCode = originalFn(source, chunk); + + // Since this is not a public API of the Vercel loader, it could + // change on patch versions and break things. + // + // If the injected code changes substantially, we throw an error + if (!originalInjectCode.includes('__webpack_require__.ab = __dirname + ')) { + throw new Error('The installed version of @vercel/webpack-asset-relocator-loader does not appear to be compatible with Forge'); + } + + return originalInjectCode.replace( + '__dirname', + this.isProd + // In production the assets are found one directory up from + // __dirname + // + // __dirname cannot be used directly until this PR lands + // https://github.com/jantimon/html-webpack-plugin/pull/1650 + ? 'require("path").resolve(require("path").dirname(__filename), "..")' + // In development, the app is loaded via webpack-dev-server + // so __dirname is useless because it points to Electron + // internal code. Instead we hard-code the absolute path to + // the webpack output. + : JSON.stringify(compiler.options.output.path), + ); + }; + } + + return tapInfo; + }, + }); + }, + ); + } +} diff --git a/packages/plugin/webpack/test/AssetRelocatorPatch_spec.ts b/packages/plugin/webpack/test/AssetRelocatorPatch_spec.ts new file mode 100644 index 0000000000..88f9c2b8d7 --- /dev/null +++ b/packages/plugin/webpack/test/AssetRelocatorPatch_spec.ts @@ -0,0 +1,228 @@ +import { Configuration, webpack } from 'webpack'; +import path from 'path'; +import { expect } from 'chai'; +import http from 'http'; +import { pathExists, readFile } from 'fs-extra'; +import { spawn } from '@malept/cross-spawn-promise'; +import { WebpackPluginConfig } from '../src/Config'; +import WebpackConfigGenerator from '../src/WebpackConfig'; + +type Closeable = { + close: () => void; +} + +let servers: Closeable[] = []; + +const nativePathSuffix = 'build/Release/hello_world.node'; +const appPath = path.join(__dirname, 'fixtures', 'apps', 'native-modules'); + +async function asyncWebpack(config: Configuration): Promise { + return new Promise((resolve, reject) => { + webpack(config, (err, stats) => { + if (err) { + reject(err); + return; + } + + if (stats?.compilation?.errors?.length) { + reject(stats.compilation.errors); + return; + } + + if (stats?.compilation?.warnings?.length) { + reject(stats.compilation.warnings); + return; + } + + resolve(); + }); + }); +} + +/** + * Webpack dev server doesn't like to exit, outputs logs so instead we just create a + * basic server. + */ +function createSimpleDevServer(rendererOut: string): http.Server { + return http.createServer(async (req, res) => { + const url = (req.url || ''); + const file = url.endsWith('main_window') ? path.join(url, '/index.html') : url; + const fullPath = path.join(rendererOut, file); + try { + const data = await readFile(fullPath); + res.writeHead(200); + res.end(data); + } catch (err) { + res.writeHead(404); + res.end(JSON.stringify(err)); + } + }).listen(3000); +} + +type ExpectNativeModulePathOptions = { + outDir: string, + jsPath: string, + nativeModulesString: string, + nativePathString: string +}; + +async function expectOutputFileToHaveTheCorrectNativeModulePath({ + outDir, + jsPath, + nativeModulesString, + nativePathString, +}: ExpectNativeModulePathOptions): Promise { + const nativePath = `native_modules/${nativePathSuffix}`; + expect(await pathExists(path.join(outDir, nativePath))).to.equal(true); + + const jsContents = await readFile(jsPath, { encoding: 'utf8' }); + expect(jsContents).to.contain(nativeModulesString); + expect(jsContents).to.contain(nativePathString); +} + +async function yarnStart(): Promise { + return spawn('yarn', ['start'], { + cwd: appPath, + shell: true, + env: { + ...process.env, + ELECTRON_ENABLE_LOGGING: 'true', + }, + }); +} + +describe('AssetRelocatorPatch', () => { + const rendererOut = path.join(appPath, '.webpack/renderer'); + const mainOut = path.join(appPath, '.webpack/main'); + + before(async () => { + await spawn('yarn', [], { cwd: appPath, shell: true }); + }); + + after(() => { + for (const server of servers) { + server.close(); + } + servers = []; + }); + + const config = { + mainConfig: './webpack.main.config.js', + renderer: { + config: './webpack.renderer.config.js', + entryPoints: [ + { + name: 'main_window', + html: './src/index.html', + js: './src/renderer.js', + preload: { + js: './src/preload.js', + }, + }, + ], + }, + } as WebpackPluginConfig; + + describe('Development', () => { + const generator = new WebpackConfigGenerator(config, appPath, false, 3000); + + it('builds main', async () => { + await asyncWebpack(generator.getMainConfig()); + + await expectOutputFileToHaveTheCorrectNativeModulePath({ + outDir: mainOut, + jsPath: path.join(mainOut, 'index.js'), + nativeModulesString: '__webpack_require__.ab = __dirname + "/native_modules/"', + nativePathString: `require(__webpack_require__.ab + "${nativePathSuffix}")`, + }); + }); + + it('builds preload', async () => { + const entryPoint = config.renderer.entryPoints[0]; + const preloadConfig = await generator.getPreloadRendererConfig( + entryPoint, entryPoint.preload!, + ); + await asyncWebpack(preloadConfig); + + await expectOutputFileToHaveTheCorrectNativeModulePath({ + outDir: path.join(rendererOut, 'main_window'), + jsPath: path.join(rendererOut, 'main_window/preload.js'), + nativeModulesString: '__webpack_require__.ab = __dirname + "/native_modules/"', + nativePathString: `require(__webpack_require__.ab + \\"${nativePathSuffix}\\")`, + }); + }); + + it('builds renderer', async () => { + const rendererConfig = await generator.getRendererConfig(config.renderer.entryPoints); + await asyncWebpack(rendererConfig); + + await expectOutputFileToHaveTheCorrectNativeModulePath({ + outDir: rendererOut, + jsPath: path.join(rendererOut, 'main_window/index.js'), + nativeModulesString: `__webpack_require__.ab = ${JSON.stringify(rendererOut)} + "/native_modules/"`, + nativePathString: `require(__webpack_require__.ab + \\"${nativePathSuffix}\\")`, + }); + }); + + it('runs the app with the native module', async () => { + servers.push(createSimpleDevServer(rendererOut)); + + const output = await yarnStart(); + + expect(output).to.contain('Hello, world! from the main'); + expect(output).to.contain('Hello, world! from the preload'); + expect(output).to.contain('Hello, world! from the renderer'); + }); + }); + + describe('Production', () => { + const generator = new WebpackConfigGenerator(config, appPath, true, 3000); + + it('builds main', async () => { + const mainConfig = generator.getMainConfig(); + await asyncWebpack(mainConfig); + + await expectOutputFileToHaveTheCorrectNativeModulePath({ + outDir: mainOut, + jsPath: path.join(mainOut, 'index.js'), + nativeModulesString: '.ab=__dirname+"/native_modules/"', + nativePathString: `.ab+"${nativePathSuffix}"`, + }); + }); + + it('builds preload', async () => { + const entryPoint = config.renderer.entryPoints[0]; + const preloadConfig = await generator.getPreloadRendererConfig( + entryPoint, entryPoint.preload!, + ); + await asyncWebpack(preloadConfig); + + await expectOutputFileToHaveTheCorrectNativeModulePath({ + outDir: path.join(rendererOut, 'main_window'), + jsPath: path.join(rendererOut, 'main_window/preload.js'), + nativeModulesString: '.ab=__dirname+"/native_modules/"', + nativePathString: `.ab+"${nativePathSuffix}"`, + }); + }); + + it('builds renderer', async () => { + const rendererConfig = await generator.getRendererConfig(config.renderer.entryPoints); + await asyncWebpack(rendererConfig); + + await expectOutputFileToHaveTheCorrectNativeModulePath({ + outDir: rendererOut, + jsPath: path.join(rendererOut, 'main_window/index.js'), + nativeModulesString: '.ab=require("path").resolve(require("path").dirname(__filename),"..")+"/native_modules/"', + nativePathString: `.ab+"${nativePathSuffix}"`, + }); + }); + + it('runs the app with the native module', async () => { + const output = await yarnStart(); + + expect(output).to.contain('Hello, world! from the main'); + expect(output).to.contain('Hello, world! from the preload'); + expect(output).to.contain('Hello, world! from the renderer'); + }); + }); +}); diff --git a/packages/plugin/webpack/test/WebpackConfig_spec.ts b/packages/plugin/webpack/test/WebpackConfig_spec.ts index 9805da01c7..614d2c79e2 100644 --- a/packages/plugin/webpack/test/WebpackConfig_spec.ts +++ b/packages/plugin/webpack/test/WebpackConfig_spec.ts @@ -1,12 +1,19 @@ -import { Entry } from 'webpack'; +import { Compiler, Entry, WebpackPluginInstance } from 'webpack'; import { expect } from 'chai'; import path from 'path'; import WebpackConfigGenerator from '../src/WebpackConfig'; import { WebpackPluginConfig, WebpackPluginEntryPoint } from '../src/Config'; +import AssetRelocatorPatch from '../src/util/AssetRelocatorPatch'; const mockProjectDir = process.platform === 'win32' ? 'C:\\path' : '/path'; +type WebpackPlugin = ((this: Compiler, compiler: Compiler) => void) | WebpackPluginInstance; + +function hasAssetRelocatorPatchPlugin(plugins?: WebpackPlugin[]): boolean { + return (plugins || []).some((plugin: WebpackPlugin) => plugin instanceof AssetRelocatorPatch); +} + describe('WebpackConfigGenerator', () => { describe('rendererTarget', () => { it('is web if undefined', () => { @@ -57,68 +64,6 @@ describe('WebpackConfigGenerator', () => { expect(() => generator.getDefines()).to.throw(/renderer.entryPoints.* has not been defined/); }); - it('sets the renderer asset relocator base dir in development', () => { - const config = { - renderer: { - entryPoints: [{ - name: 'hello', - js: 'foo.js', - }], - }, - } as WebpackPluginConfig; - const generator = new WebpackConfigGenerator(config, '/', false, 3000); - const defines = generator.getDefines(false); - - expect(defines.ASSET_RELOCATOR_BASE_DIR).to.equal(JSON.stringify(path.resolve('/.webpack', 'renderer', 'any_folder'))); - }); - - it('sets the renderer asset relocator base dir in production', () => { - const config = { - renderer: { - entryPoints: [{ - name: 'hello', - js: 'foo.js', - }], - }, - } as WebpackPluginConfig; - const generator = new WebpackConfigGenerator(config, '/', true, 3000); - const defines = generator.getDefines(false); - - // eslint-disable-next-line no-template-curly-in-string - expect(defines.ASSET_RELOCATOR_BASE_DIR).to.equal('process.resourcesPath + "/" + (__filename.includes(".asar") ? "app.asar" : "app") + "/.webpack/renderer/any_folder"'); - }); - - it('sets the main asset relocator base dir in development', () => { - const config = { - renderer: { - entryPoints: [{ - name: 'hello', - js: 'foo.js', - }], - }, - } as WebpackPluginConfig; - const generator = new WebpackConfigGenerator(config, '/', false, 3000); - const defines = generator.getDefines(true); - - expect(defines.ASSET_RELOCATOR_BASE_DIR).to.equal(JSON.stringify(path.resolve('/.webpack', 'main', '.'))); - }); - - it('sets the main asset relocator base dir in production', () => { - const config = { - renderer: { - entryPoints: [{ - name: 'hello', - js: 'foo.js', - }], - }, - } as WebpackPluginConfig; - const generator = new WebpackConfigGenerator(config, '/', true, 3000); - const defines = generator.getDefines(true); - - // eslint-disable-next-line no-template-curly-in-string - expect(defines.ASSET_RELOCATOR_BASE_DIR).to.equal('process.resourcesPath + "/" + (__filename.includes(".asar") ? "app.asar" : "app") + "/.webpack/main"'); - }); - it('sets the renderer entry point to a JS file in development', () => { const config = { renderer: { @@ -230,6 +175,7 @@ describe('WebpackConfigGenerator', () => { filename: 'index.js', libraryTarget: 'commonjs2', }); + expect(hasAssetRelocatorPatchPlugin(webpackConfig.plugins)).to.equal(false); }); it('generates a production config', async () => { @@ -244,6 +190,7 @@ describe('WebpackConfigGenerator', () => { const generator = new WebpackConfigGenerator(config, mockProjectDir, true, 3000); const webpackConfig = await generator.getMainConfig(); expect(webpackConfig.mode).to.equal('production'); + expect(hasAssetRelocatorPatchPlugin(webpackConfig.plugins)).to.equal(false); }); it('generates a config with a relative entry path', async () => { @@ -319,6 +266,7 @@ describe('WebpackConfigGenerator', () => { path: path.join(mockProjectDir, '.webpack', 'renderer', 'main'), filename: 'preload.js', }); + expect(hasAssetRelocatorPatchPlugin(webpackConfig.plugins)).to.equal(false); }); it('generates a production config', async () => { @@ -345,6 +293,7 @@ describe('WebpackConfigGenerator', () => { path: path.join(mockProjectDir, '.webpack', 'renderer', 'main'), filename: 'preload.js', }); + expect(hasAssetRelocatorPatchPlugin(webpackConfig.plugins)).to.equal(false); }); it('prevents the preload target from being overridden', async () => { const config = { @@ -394,7 +343,8 @@ describe('WebpackConfigGenerator', () => { globalObject: 'self', publicPath: '/', }); - expect(webpackConfig.plugins!.length).to.equal(1); + expect(webpackConfig.plugins!.length).to.equal(2); + expect(hasAssetRelocatorPatchPlugin(webpackConfig.plugins)).to.equal(true); }); it('generates a development config with an HTML endpoint', async () => { @@ -414,7 +364,8 @@ describe('WebpackConfigGenerator', () => { 'rendererScript.js', ], }); - expect(webpackConfig.plugins!.length).to.equal(2); + expect(webpackConfig.plugins!.length).to.equal(3); + expect(hasAssetRelocatorPatchPlugin(webpackConfig.plugins)).to.equal(true); }); it('generates a production config', async () => { @@ -438,7 +389,8 @@ describe('WebpackConfigGenerator', () => { filename: '[name]/index.js', globalObject: 'self', }); - expect(webpackConfig.plugins!.length).to.equal(1); + expect(webpackConfig.plugins!.length).to.equal(2); + expect(hasAssetRelocatorPatchPlugin(webpackConfig.plugins)).to.equal(true); }); it('can override the renderer target', async () => { diff --git a/packages/plugin/webpack/test/fixtures/apps/native-modules/package.json b/packages/plugin/webpack/test/fixtures/apps/native-modules/package.json new file mode 100644 index 0000000000..64d67c2549 --- /dev/null +++ b/packages/plugin/webpack/test/fixtures/apps/native-modules/package.json @@ -0,0 +1,16 @@ +{ + "name": "native-app", + "version": "1.0.0", + "main": "./.webpack/main/index.js", + "license": "MIT", + "scripts": { + "start": "electron ." + }, + "devDependencies": { + "@vercel/webpack-asset-relocator-loader": "1.5.0", + "electron": "13.1.2" + }, + "dependencies": { + "native-hello-world": "^2.0.0" + } +} diff --git a/packages/plugin/webpack/test/fixtures/apps/native-modules/src/index.html b/packages/plugin/webpack/test/fixtures/apps/native-modules/src/index.html new file mode 100644 index 0000000000..6c595e4f36 --- /dev/null +++ b/packages/plugin/webpack/test/fixtures/apps/native-modules/src/index.html @@ -0,0 +1,5 @@ + + + Test + + diff --git a/packages/plugin/webpack/test/fixtures/apps/native-modules/src/index.js b/packages/plugin/webpack/test/fixtures/apps/native-modules/src/index.js new file mode 100644 index 0000000000..910a3c13be --- /dev/null +++ b/packages/plugin/webpack/test/fixtures/apps/native-modules/src/index.js @@ -0,0 +1,33 @@ +import { app, BrowserWindow, ipcMain } from 'electron'; + +let count = 0; +ipcMain.on('stdout', (_, line) => { + console.log(line); + count += 1; + + if (count > 1) { + process.exit(); + } +}); + +app.on('ready', () => { + const mainWindow = new BrowserWindow({ + height: 600, + width: 800, + show: false, + webPreferences: { + nodeIntegration: true, + contextIsolation: false, + preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY, + }, + }); + + mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY); +}); + +const helloWorld = require('native-hello-world'); +console.log(`${helloWorld()} from the main`); + +setTimeout(() => { + process.exit(); +}, 10000); diff --git a/packages/plugin/webpack/test/fixtures/apps/native-modules/src/preload.js b/packages/plugin/webpack/test/fixtures/apps/native-modules/src/preload.js new file mode 100644 index 0000000000..6fcf702519 --- /dev/null +++ b/packages/plugin/webpack/test/fixtures/apps/native-modules/src/preload.js @@ -0,0 +1,4 @@ +import { ipcRenderer } from 'electron'; + +const helloWorld = require('native-hello-world'); +ipcRenderer.send('stdout', `${helloWorld()} from the preload`); diff --git a/packages/plugin/webpack/test/fixtures/apps/native-modules/src/renderer.js b/packages/plugin/webpack/test/fixtures/apps/native-modules/src/renderer.js new file mode 100644 index 0000000000..7ed63bca11 --- /dev/null +++ b/packages/plugin/webpack/test/fixtures/apps/native-modules/src/renderer.js @@ -0,0 +1,4 @@ +import { ipcRenderer } from 'electron'; + +const helloWorld = require('native-hello-world'); +ipcRenderer.send('stdout', `${helloWorld()} from the renderer`); diff --git a/packages/plugin/webpack/test/fixtures/apps/native-modules/webpack.main.config.js b/packages/plugin/webpack/test/fixtures/apps/native-modules/webpack.main.config.js new file mode 100644 index 0000000000..dd0ed74cba --- /dev/null +++ b/packages/plugin/webpack/test/fixtures/apps/native-modules/webpack.main.config.js @@ -0,0 +1,13 @@ +module.exports = { + context: __dirname, + entry: './src/index.js', + performance: { + hints: false, + }, + module: { + rules: require('./webpack.rules'), + }, + resolve: { + extensions: ['.js'], + }, +}; diff --git a/packages/plugin/webpack/test/fixtures/apps/native-modules/webpack.renderer.config.js b/packages/plugin/webpack/test/fixtures/apps/native-modules/webpack.renderer.config.js new file mode 100644 index 0000000000..891dac3512 --- /dev/null +++ b/packages/plugin/webpack/test/fixtures/apps/native-modules/webpack.renderer.config.js @@ -0,0 +1,15 @@ +const rules = require('./webpack.rules'); + +module.exports = { + context: __dirname, + target: 'electron-renderer', + performance: { + hints: false, + }, + module: { + rules, + }, + resolve: { + extensions: ['.js'], + }, +}; diff --git a/packages/plugin/webpack/test/fixtures/apps/native-modules/webpack.rules.js b/packages/plugin/webpack/test/fixtures/apps/native-modules/webpack.rules.js new file mode 100644 index 0000000000..413847ad03 --- /dev/null +++ b/packages/plugin/webpack/test/fixtures/apps/native-modules/webpack.rules.js @@ -0,0 +1,12 @@ +module.exports = [ + { + test: /\.(m?js|node)$/, + parser: { amd: false }, + use: { + loader: '@vercel/webpack-asset-relocator-loader', + options: { + outputAssetBase: 'native_modules', + }, + }, + }, +]; diff --git a/packages/template/typescript-webpack/tmpl/package.json b/packages/template/typescript-webpack/tmpl/package.json index c22c10ef65..16ee50f495 100644 --- a/packages/template/typescript-webpack/tmpl/package.json +++ b/packages/template/typescript-webpack/tmpl/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "@electron-forge/plugin-webpack": "ELECTRON_FORGE/VERSION", - "@marshallofsound/webpack-asset-relocator-loader": "^0.5.0", + "@vercel/webpack-asset-relocator-loader": "1.5.0", "css-loader": "^5.0.0", "node-loader": "^2.0.0", "ts-loader": "^9.2.2", diff --git a/packages/template/typescript-webpack/tmpl/webpack.rules.js b/packages/template/typescript-webpack/tmpl/webpack.rules.js index 56f2d55a9c..8069b8e3dd 100644 --- a/packages/template/typescript-webpack/tmpl/webpack.rules.js +++ b/packages/template/typescript-webpack/tmpl/webpack.rules.js @@ -8,7 +8,7 @@ module.exports = [ test: /\.(m?js|node)$/, parser: { amd: false }, use: { - loader: '@marshallofsound/webpack-asset-relocator-loader', + loader: '@vercel/webpack-asset-relocator-loader', options: { outputAssetBase: 'native_modules', }, diff --git a/packages/template/webpack/src/WebpackTemplate.ts b/packages/template/webpack/src/WebpackTemplate.ts index 18df4ed5cc..3b60deee37 100644 --- a/packages/template/webpack/src/WebpackTemplate.ts +++ b/packages/template/webpack/src/WebpackTemplate.ts @@ -4,7 +4,6 @@ import fs from 'fs-extra'; import { InitTemplateOptions } from '@electron-forge/shared-types'; import path from 'path'; -// TODO: Use the @zeit publish once https://github.com/zeit/webpack-asset-relocator-loader/pull/41 has been merged class WebpackTemplate extends BaseTemplate { public templateDir = path.resolve(__dirname, '..', 'tmpl'); diff --git a/packages/template/webpack/tmpl/package.json b/packages/template/webpack/tmpl/package.json index f54be6d778..1704e5ec68 100644 --- a/packages/template/webpack/tmpl/package.json +++ b/packages/template/webpack/tmpl/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "@electron-forge/plugin-webpack": "ELECTRON_FORGE/VERSION", - "@marshallofsound/webpack-asset-relocator-loader": "^0.5.0", + "@vercel/webpack-asset-relocator-loader": "1.5.0", "css-loader": "^5.0.0", "node-loader": "^2.0.0", "style-loader": "^3.0.0" diff --git a/packages/template/webpack/tmpl/webpack.rules.js b/packages/template/webpack/tmpl/webpack.rules.js index 2a98d46cf8..219bb313e7 100644 --- a/packages/template/webpack/tmpl/webpack.rules.js +++ b/packages/template/webpack/tmpl/webpack.rules.js @@ -8,7 +8,7 @@ module.exports = [ test: /\.(m?js|node)$/, parser: { amd: false }, use: { - loader: '@marshallofsound/webpack-asset-relocator-loader', + loader: '@vercel/webpack-asset-relocator-loader', options: { outputAssetBase: 'native_modules', }, diff --git a/yarn.lock b/yarn.lock index 08eb66b548..db2b292b27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10219,7 +10219,7 @@ which@2.0.2, which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -which@^1.2.14, which@^1.2.9: +which@^1.2.14, which@^1.2.4, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -10373,6 +10373,14 @@ xterm@^4.9.0: resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.13.0.tgz#7998de1e2ad92c4796fe45807be4f31061f3d9d1" integrity sha512-HVW1gdoLOTnkMaqQCr2r3mQy4fX9iSa5gWxKZ2UTYdLa4iqavv7QxJ8n1Ypse32shPVkhTYPLS6vHEFZp5ghzw== +xvfb-maybe@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/xvfb-maybe/-/xvfb-maybe-0.2.1.tgz#ed8cb132957b7848b439984c66f010ea7f24361b" + integrity sha1-7YyxMpV7eEi0OZhMZvAQ6n8kNhs= + dependencies: + debug "^2.2.0" + which "^1.2.4" + y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"