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

Fix ./run gui watch #6212

Merged
merged 11 commits into from
Apr 11, 2023
2 changes: 0 additions & 2 deletions app/ide-desktop/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,6 @@ export default [
BUNDLED_ENGINE_VERSION: true,
PROJECT_MANAGER_IN_BUNDLE_PATH: true,
BUILD_INFO: true,
// Used in `lib/copy-plugin/src/index.mjs`.
AsyncGenerator: true,
},
},
rules: {
Expand Down
9 changes: 4 additions & 5 deletions app/ide-desktop/lib/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,16 @@
"electron-builder": "^22.14.13",
"electron-notarize": "1.2.2",
"enso-common": "^1.0.0",
"enso-copy-plugin": "^1.0.0",
"esbuild": "^0.17.0",
"esbuild": "^0.17.15",
"fast-glob": "^3.2.12",
"portfinder": "^1.0.32",
"tsx": "^3.12.6"
},
"optionalDependencies": {
"dmg-license": "^1.0.11",
"@esbuild/darwin-x64": "^0.17.0",
"@esbuild/linux-x64": "^0.17.0",
"@esbuild/windows-x64": "^0.17.0"
"@esbuild/darwin-x64": "^0.17.15",
"@esbuild/linux-x64": "^0.17.15",
"@esbuild/windows-x64": "^0.17.15"
},
"scripts": {
"typecheck": "tsc --noEmit",
Expand Down
86 changes: 47 additions & 39 deletions app/ide-desktop/lib/content/esbuild-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,22 @@

import * as childProcess from 'node:child_process'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import * as fsSync from 'node:fs'
import * as pathModule from 'node:path'
import * as url from 'node:url'

import * as esbuild from 'esbuild'
import * as esbuildPluginCopy from 'enso-copy-plugin'
import * as esbuildPluginNodeGlobals from '@esbuild-plugins/node-globals-polyfill'
import * as esbuildPluginNodeModules from '@esbuild-plugins/node-modules-polyfill'
import esbuildPluginAlias from 'esbuild-plugin-alias'
import esbuildPluginCopyDirectories from 'esbuild-plugin-copy-directories'
import esbuildPluginTime from 'esbuild-plugin-time'
import esbuildPluginYaml from 'esbuild-plugin-yaml'

import * as utils from '../../utils.js'
import * as utils from '../../utils'
import BUILD_INFO from '../../build.json' assert { type: 'json' }

export const THIS_PATH = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)))
export const THIS_PATH = pathModule.resolve(pathModule.dirname(url.fileURLToPath(import.meta.url)))

// =============================
// === Environment variables ===
Expand All @@ -40,6 +41,8 @@ export interface Arguments {
outputPath: string
/** The main JS bundle to load WASM and JS wasm-pack bundles. */
ensoglAppPath: string
/** `true` if in development mode (live-reload), `false` if in production mode. */
devMode: boolean
}

/**
Expand All @@ -48,9 +51,9 @@ export interface Arguments {
export function argumentsFromEnv(): Arguments {
const wasmArtifacts = utils.requireEnv('ENSO_BUILD_GUI_WASM_ARTIFACTS')
const assetsPath = utils.requireEnv('ENSO_BUILD_GUI_ASSETS')
const outputPath = path.resolve(utils.requireEnv('ENSO_BUILD_GUI'), 'assets')
const outputPath = pathModule.resolve(utils.requireEnv('ENSO_BUILD_GUI'), 'assets')
const ensoglAppPath = utils.requireEnv('ENSO_BUILD_GUI_ENSOGL_APP')
return { wasmArtifacts, assetsPath, outputPath, ensoglAppPath }
return { wasmArtifacts, assetsPath, outputPath, ensoglAppPath, devMode: false }
}

// ===================
Expand All @@ -68,36 +71,6 @@ function git(command: string): string {
return childProcess.execSync(`git ${command}`, { encoding: 'utf8' }).trim()
}

// ==============================
// === Files to manually copy ===
// ==============================

/**
* Static set of files that are always copied to the output directory.
*/
export function alwaysCopiedFiles(wasmArtifacts: string) {
return [
path.resolve(THIS_PATH, 'src', 'index.html'),
path.resolve(THIS_PATH, 'src', 'run.js'),
path.resolve(THIS_PATH, 'src', 'style.css'),
path.resolve(THIS_PATH, 'src', 'docsStyle.css'),
...wasmArtifacts.split(path.delimiter),
]
}

/**
* Generator that yields all files that should be copied to the output directory.
* @yields {string} The file path of the next file to be copied.
*/
export async function* filesToCopyProvider(wasmArtifacts: string, assetsPath: string) {
console.log('Preparing a new generator for files to copy.')
yield* alwaysCopiedFiles(wasmArtifacts)
for (const file of await fs.readdir(assetsPath)) {
yield path.resolve(assetsPath, file)
}
console.log('Generator for files to copy finished.')
}

// ================
// === Bundling ===
// ================
Expand All @@ -106,27 +79,62 @@ export async function* filesToCopyProvider(wasmArtifacts: string, assetsPath: st
* Generate the builder options.
*/
export function bundlerOptions(args: Arguments) {
const { outputPath, ensoglAppPath, wasmArtifacts, assetsPath } = args
const { outputPath, ensoglAppPath, wasmArtifacts, assetsPath, devMode } = args
const buildOptions = {
// Disabling naming convention because these are third-party options.
/* eslint-disable @typescript-eslint/naming-convention */
absWorkingDir: THIS_PATH,
bundle: true,
entryPoints: [path.resolve(THIS_PATH, 'src', 'index.ts')],
loader: {
'.html': 'copy',
'.css': 'copy',
'.wasm': 'copy',
'.svg': 'copy',
'.png': 'copy',
'.ttf': 'copy',
},
entryPoints: [
pathModule.resolve(THIS_PATH, 'src', 'index.ts'),
pathModule.resolve(THIS_PATH, 'src', 'index.html'),
pathModule.resolve(THIS_PATH, 'src', 'run.js'),
pathModule.resolve(THIS_PATH, 'src', 'style.css'),
pathModule.resolve(THIS_PATH, 'src', 'docsStyle.css'),
...wasmArtifacts.split(pathModule.delimiter),
...fsSync
.readdirSync(assetsPath)
.map(fileName => pathModule.resolve(assetsPath, fileName)),
].map(path => ({ in: path, out: pathModule.basename(path, pathModule.extname(path)) })),
outdir: outputPath,
outbase: 'src',
plugins: [
{
// This is a workaround that is needed
// because esbuild panics when using `loader: { '.js': 'copy' }`.
// See https://github.com/evanw/esbuild/issues/3041.
// Setting `loader: 'copy'` prevents this file from being converted to ESM
// because of the `"type": "module"` in the `package.json`.
// This file MUST be in CommonJS format because it is loaded using `Function()`
// in `ensogl/pack/js/src/runner/index.ts`
name: 'pkg-js-is-cjs',
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does setting loader to copy mean the file "is cjs"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

will add comment - loader: copy prevents esbuild from processing the file, otherwise it becomes ESM which breaks the way we load the file

setup: build => {
build.onLoad({ filter: /\/pkg.js$/ }, async ({ path }) => ({
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we add a special rule just for pkg.js? Please document, how pkg.js is special.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

will do.
(explaining here too for posterity:)
esbuild converts files to ESM because the package.json says so, however pkg.js is loaded using new Function in wasm-pack code, i believe - so the default output (pkg.js being a module) throws a syntax error

contents: await fs.readFile(path),
loader: 'copy',
}))
},
},
esbuildPluginCopyDirectories(),
esbuildPluginYaml.yamlPlugin({}),
esbuildPluginNodeModules.NodeModulesPolyfillPlugin(),
esbuildPluginNodeGlobals.NodeGlobalsPolyfillPlugin({ buffer: true, process: true }),
esbuildPluginAlias({ ensogl_app: ensoglAppPath }),
esbuildPluginTime(),
esbuildPluginCopy.create(() => filesToCopyProvider(wasmArtifacts, assetsPath)),
],
define: {
GIT_HASH: JSON.stringify(git('rev-parse HEAD')),
GIT_STATUS: JSON.stringify(git('status --short --porcelain')),
BUILD_INFO: JSON.stringify(BUILD_INFO),
IS_DEV_MODE: JSON.stringify(devMode),
},
sourcemap: true,
minify: true,
Expand Down
10 changes: 5 additions & 5 deletions app/ide-desktop/lib/content/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
"enso-authentication": "^1.0.0",
"enso-copy-plugin": "^1.0.0",
"esbuild": "^0.17.0",
"esbuild": "^0.17.15",
"esbuild-plugin-alias": "^0.2.1",
"esbuild-plugin-copy-directories": "^1.0.0",
"esbuild-plugin-time": "^1.0.0",
"esbuild-plugin-yaml": "^0.0.1",
"eslint": "^8.36.0",
Expand All @@ -51,8 +51,8 @@
"typescript": "^4.9.3"
},
"optionalDependencies": {
"@esbuild/darwin-x64": "^0.17.0",
"@esbuild/linux-x64": "^0.17.0",
"@esbuild/windows-x64": "^0.17.0"
"@esbuild/darwin-x64": "^0.17.15",
"@esbuild/linux-x64": "^0.17.15",
"@esbuild/windows-x64": "^0.17.15"
}
}
18 changes: 18 additions & 0 deletions app/ide-desktop/lib/content/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,29 @@ import GLOBAL_CONFIG from '../../../../gui/config.yaml' assert { type: 'yaml' }

const logger = app.log.logger

// =================
// === Constants ===
// =================

/** Path to the SSE endpoint over which esbuild sends events. */
const ESBUILD_PATH = '/esbuild'
/** SSE event indicating a build has finished. */
const ESBUILD_EVENT_NAME = 'change'
/** One second in milliseconds. */
const SECOND = 1000
/** Time in seconds after which a `fetchTimeout` ends. */
const FETCH_TIMEOUT = 300

// ===================
// === Live reload ===
// ===================

if (IS_DEV_MODE) {
new EventSource(ESBUILD_PATH).addEventListener(ESBUILD_EVENT_NAME, () => {
location.reload()
})
}

// =============
// === Fetch ===
// =============
Expand Down
10 changes: 8 additions & 2 deletions app/ide-desktop/lib/content/watch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** @file File watch and compile service. */
import * as esbuild from 'esbuild'
import * as portfinder from 'portfinder'
import chalk from 'chalk'

import * as bundler from './esbuild-config'
import * as dashboardBundler from '../dashboard/esbuild-config'
Expand All @@ -24,15 +25,20 @@ async function watch() {
// This MUST be called before `builder.watch()` as `tailwind.css` must be generated
// before the copy plugin runs.
await dashboardBuilder.watch()
const opts = bundler.bundleOptions()
const opts = bundler.bundlerOptions({
...bundler.argumentsFromEnv(),
devMode: true,
})
const builder = await esbuild.context(opts)
await builder.watch()
await builder.serve({
port: await portfinder.getPortPromise({ port: PORT }),
servedir: opts.outdir,
onRequest(args) {
if (args.status !== HTTP_STATUS_OK) {
console.error(`HTTP error ${args.status} when serving path '${args.path}'.`)
console.error(
chalk.red(`HTTP error ${args.status} when serving path '${args.path}'.`)
)
}
},
})
Expand Down
12 changes: 0 additions & 12 deletions app/ide-desktop/lib/copy-plugin/package.json

This file was deleted.

110 changes: 0 additions & 110 deletions app/ide-desktop/lib/copy-plugin/src/index.mjs

This file was deleted.