diff --git a/docs/.vitepress/sidebar.config.mts b/docs/.vitepress/sidebar.config.mts index 3311e09..acd7e43 100755 --- a/docs/.vitepress/sidebar.config.mts +++ b/docs/.vitepress/sidebar.config.mts @@ -54,6 +54,10 @@ export const SIDEBAR: DefaultTheme.Sidebar = [ text: 'Missing Config File', link: 'missing-config-file', }, + { + text: 'ESM issues', + link: 'esm-issues', + }, { text: 'Empty Glob', link: 'empty-glob', diff --git a/docs/app/quick-start.md b/docs/app/quick-start.md index b98c712..912b578 100755 --- a/docs/app/quick-start.md +++ b/docs/app/quick-start.md @@ -28,8 +28,8 @@ module.exports = {} 1. Create a Workflow via Alfred UI, or use an existing one 1. Open the Workflow directory, copy relevant files (icons, `info.plist`, `prefs.plist`, etc) to your project -1. Set `"type": "module"` in your `package.json` file - this is required for - using [`fast-alfred` runtime](./setup/runtime-explain) +1. In case your package declares `"type": "module"` in the `package.json` file, you'll + need to set both [`esmHelpers`](./setup/bundler-options#esmhelpers) and [`outputFormat`](./setup/bundler-options#outputformat) in order to use [`fast-alfred` runtime](./setup/runtime-explain) 1. Create a source directory for your Workflow scripts ```bash diff --git a/docs/app/setup/bundler-options.md b/docs/app/setup/bundler-options.md index 2adc11d..296ac21 100644 --- a/docs/app/setup/bundler-options.md +++ b/docs/app/setup/bundler-options.md @@ -100,10 +100,16 @@ module.exports = { } ``` -## `includeBanners` +## `esmHelpers` -By default, `fast-alfred` provides you with some compatibilities between CommonJS and ES Modules. -You can disable this feature by setting the `includeBanners` property to `false`. +By default, `fast-alfred` aims to be written in the `cjs` Module format. +If your package is using the `esm` format, AKA, having `"type": "module"` in the `package.json`, you should enable the `esmHelpers` property. + +::: warning Warning :warning: +In case your package is written in `cjs`, enabling this option would break your build. + +**Note that you should also set the [`outputFormat` property](#outputformat) to `esm` to output the files in the ES Module format.** +::: ##### Example @@ -113,15 +119,20 @@ You can disable this feature by setting the `includeBanners` property to `false` */ module.exports = { bundlerOptions: { - includeBanners: false, // Disable the compatibility banners + esmHelpers: true, // Enable compatibility for CommonJS packages }, } ``` -## `minify` +## `outputFormat` -By default, `fast-alfred` would minify the output files. -It might be useful to disable this feature for debugging purposes. +By default, `fast-alfred` would output the files in the `cjs` format. +You can change that by setting the `outputFormat` property. + +::: tip TIP :zap: +If your `package.json` has `"type": "module"`, you should output the files in the `esm` format. +Otherwise, you should keep it in the `cjs` format. +::: ##### Example @@ -131,20 +142,15 @@ It might be useful to disable this feature for debugging purposes. */ module.exports = { bundlerOptions: { - minify: false, // Disable minification + outputFormat: 'esm', // Output files in the ES Module format }, } ``` -## `treeShaking` - -By default, `fast-alfred` would tree-shake the output files. That way, only the necessary code will be included in the final bundle. -You can disable this feature by setting the `treeShaking` property to `false`. +## `minify` -::: warning Warning :warning: -Disabling tree shaking might increase the bundle size. -You should only disable tree shaking if you're facing issues with the output bundle. -::: +By default, `fast-alfred` would minify the output files. +It might be useful to disable this feature for debugging purposes. ##### Example @@ -154,15 +160,20 @@ You should only disable tree shaking if you're facing issues with the output bun */ module.exports = { bundlerOptions: { - treeShaking: false, // Disable tree shaking + minify: false, // Disable minification }, } ``` -## `outputFormat` +## `treeShaking` -By default, `fast-alfred` would output the files in the `cjs` format. -You can change that by setting the `outputFormat` property. +By default, `fast-alfred` would tree-shake the output files. That way, only the necessary code will be included in the final bundle. +You can disable this feature by setting the `treeShaking` property to `false`. + +::: warning Warning :warning: +Disabling tree shaking might increase the bundle size. +You should only disable tree shaking if you're facing issues with the output bundle. +::: ##### Example @@ -172,7 +183,7 @@ You can change that by setting the `outputFormat` property. */ module.exports = { bundlerOptions: { - outputFormat: 'esm', // Output files in the ES Module format + treeShaking: false, // Disable tree shaking }, } ``` diff --git a/docs/app/setup/runtime-explain.md b/docs/app/setup/runtime-explain.md index 8fc0f0d..05838bd 100644 --- a/docs/app/setup/runtime-explain.md +++ b/docs/app/setup/runtime-explain.md @@ -10,8 +10,7 @@ next: true The main advantage of this approach is that your Node.js script would be executed in an environment that is aware of the Alfred workflow, and it will be able to interact with it, as well as having caching and other features. ::: warning NOTE :rotating_light: -In order to use `fast-alfred` runtime, you have to configure your package as ESM module. -Add `"type": "module"` to your `package.json` file. +In order to use `fast-alfred` runtime in ESM format, you need to configure these two options [`esmHelpers`](./bundler-options#esmhelpers) and [`outputFormat`](./bundler-options#outputformat). ::: ## How It Works @@ -37,7 +36,37 @@ The code below is an example of how to trigger your Node.js script in an Alfred Sometimes, we just want to run scripts locally, and put some debugger breakpoints to understand the flow. -In case you're using [`typescript` paths](https://www.typescriptlang.org/tsconfig/#paths), you might need to convert them after being built (without `fast-alfred` bundler). +### `CommonJS` Format + +In case you're using the [`CommonJS` format](./bundler-options#outputformat), you can run the following command: + +```bash +ts-node ./src/main/your-script-under-main.ts +``` + +::: tip TIP :zap: +In case you're using [`typescript` paths](https://www.typescriptlang.org/tsconfig/#paths), you might need to convert them, without the help of `fast-alfred` bundler. +Add the following to your `tsconfig.json` file:, + +```json + "ts-node": { + "require": ["tsconfig-paths/register"] + }, +``` + +And install the `tsconfig-paths` package: + +```bash +npm i -D tsconfig-paths +``` + +::: + +### `ESM` Format + +For the `ESM` format, `ts-node` is known to have some issues with it. + +In case you're using [`typescript` paths](https://www.typescriptlang.org/tsconfig/#paths), you'll have to convert them is a separate step. ::: warning NOTE :rotating_light: You need to install the `tsc-alias` package to convert the paths @@ -68,5 +97,5 @@ node ./dist/your-script-under-main.js The code above is an example of how to trigger your Node.js script in a local environment, right from your IDE. -**You can place your breakpoints and debug your script from your .ts file.** +**You can place your breakpoints and debug your script from your .ts file (require `sourceMap`)** ::: diff --git a/docs/app/troubleshooting/empty-glob.md b/docs/app/troubleshooting/empty-glob.md index 2f8756e..ba50966 100644 --- a/docs/app/troubleshooting/empty-glob.md +++ b/docs/app/troubleshooting/empty-glob.md @@ -1,7 +1,7 @@ --- prev: - text: 'Missing Configuration File' - link: '/app/troubleshooting/missing-config-file' + text: 'ESM issues' + link: '/app/troubleshooting/esm-issues' next: text: 'No Target Version' link: '/app/troubleshooting/no-target-version' diff --git a/docs/app/troubleshooting/esm-issues.md b/docs/app/troubleshooting/esm-issues.md new file mode 100644 index 0000000..df7bb73 --- /dev/null +++ b/docs/app/troubleshooting/esm-issues.md @@ -0,0 +1,66 @@ +--- +prev: + text: 'Missing Configuration File' + link: '/app/troubleshooting/missing-config-file' +next: + text: 'Empty Glob' + link: '/app/troubleshooting/empty-glob' +--- + +# ESM issues + +`fast-alfred` by default bundles your scripts in the `cjs` format. +In case your script uses ESM syntax, you might encounter the following error: + +```log +SyntaxError: Cannot use import statement outside a module +``` + +## Solution + +### Make sure no ESM syntax is used in your build + +`fast-alfred` provides nice [helpers to EMS syntax](../setup/bundler-options#esmhelpers), in order to have compatibility with CommonJS packages. +Make sure this option disabled in case you're using the `cjs` format. +Also, verify that the `outputFormat` is set to `cjs`. + +::: code-group + +```javascript [.fast-alfred.config.cjs] +/** + * @type {import('fast-alfred').FastAlfredConfig} + */ +module.exports = { + bundlerOptions: { + // Both should be the default + esmHelpers: false, // Disable compatibility for CommonJS packages + outputFormat: 'cjs', // Output files in the CommonJS format + }, +} +``` + +::: + +### Migrate to ESM + +You can change the output format to `esm` by adding the following configuration to your `.fast-alfred.config.cjs` file. + +::: warning NOTE :warning: +Your `package.json` should have `"type": "module"` in order to use the ESM format. +::: + +::: code-group + +```javascript [.fast-alfred.config.cjs] +/** + * @type {import('fast-alfred').FastAlfredConfig} + */ +module.exports = { + bundlerOptions: { + outputFormat: 'esm', // Change the output format to ESM + esmHelpers: true, // Enable compatibility for CommonJS packages - not mandatory, but recommended + }, +} +``` + +::: diff --git a/docs/app/troubleshooting/missing-config-file.md b/docs/app/troubleshooting/missing-config-file.md index e06550b..3b06bed 100755 --- a/docs/app/troubleshooting/missing-config-file.md +++ b/docs/app/troubleshooting/missing-config-file.md @@ -1,8 +1,8 @@ --- prev: false next: - text: 'Empty Glob' - link: '/app/troubleshooting/empty-glob' + text: 'ESM issues' + link: '/app/troubleshooting/esm-issues' --- # Missing Configuration File diff --git a/src/bundler/constants/bundler-options-defaults.config.ts b/src/bundler/constants/bundler-options-defaults.config.ts index 2026fb1..9c66cfb 100644 --- a/src/bundler/constants/bundler-options-defaults.config.ts +++ b/src/bundler/constants/bundler-options-defaults.config.ts @@ -6,7 +6,7 @@ export const BUNDLER_DEFAULTS: Required = { assetsDir: 'assets', targetDir: 'esbuild', productionScripts: ['src/main/*.ts'], - includeBanners: true, + esmHelpers: false, minify: true, treeShaking: true, outputFormat: 'cjs', @@ -21,10 +21,10 @@ export const ALL_FRAMEWORK_ASSETS = [ ] /** - * Fix common issues when compiling both `esm` and `cjs` modules. + * @description + * This code would be included in the bundle, in order to resolve common issues when compiling both `esm` and `cjs` modules. */ -export const DEFAULT_BANNERS = { - js: ` +export const ESM_HELPERS = ` import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); import _private_path from 'node:path'; @@ -33,8 +33,7 @@ import _private_url from 'node:url'; globalThis.require = createRequire(import.meta.url); globalThis.__filename = _private_url.fileURLToPath(import.meta.url); globalThis.__dirname = _private_path.dirname(__filename); -`, -} as const +` as const export const PACK_ENTITIES = (targetDir: string) => ['*.png ', '*.plist', 'README.md', `${targetDir}/**`, 'package.json'] as const diff --git a/src/bundler/models/bundler-options.model.ts b/src/bundler/models/bundler-options.model.ts index 0ba8e8c..4ace515 100644 --- a/src/bundler/models/bundler-options.model.ts +++ b/src/bundler/models/bundler-options.model.ts @@ -38,12 +38,12 @@ export interface BundlerOptions { /** * @description - * Whether to include JS banners in the bundle. - * Headers should resolve common issues when compiling both `esm` and `cjs` modules. + * Whether to include JS code in the bundle, that resolve common issues when compiling both `esm` and `cjs` modules. + * Most issues would be resolving `require`, `__dirname` and `__filename` variables. * - * @default true + * @default false */ - includeBanners?: boolean + esmHelpers?: boolean /** * @description diff --git a/src/bundler/services/build-workflow.service.ts b/src/bundler/services/build-workflow.service.ts index bfb36d5..3726b30 100755 --- a/src/bundler/services/build-workflow.service.ts +++ b/src/bundler/services/build-workflow.service.ts @@ -2,7 +2,7 @@ import * as esbuild from 'esbuild' import { basename } from 'node:path' import { execPromise } from '@common/utils' import { readWorkflowPackageJson } from '@common/workflow-package-json.service' -import { ALL_FRAMEWORK_ASSETS, DEFAULT_BANNERS, PACK_ENTITIES } from '../constants/bundler-options-defaults.config' +import { ALL_FRAMEWORK_ASSETS, ESM_HELPERS, PACK_ENTITIES } from '../constants/bundler-options-defaults.config' import { buildOptions, cleanTarget, copyAssets } from '../utils/bundler.utils' export async function buildWorkflow() { @@ -10,7 +10,7 @@ export async function buildWorkflow() { const { assets, assetsDir, - includeBanners, + esmHelpers, minify, outputFormat, overrideEsbuildOptions, @@ -23,6 +23,10 @@ export async function buildWorkflow() { await cleanTarget(targetDir) + const banners = { + js: esmHelpers ? ESM_HELPERS : '', + } + await esbuild.build({ platform: 'node', entryPoints: productionScripts, @@ -31,7 +35,7 @@ export async function buildWorkflow() { treeShaking, minify, format: outputFormat, - banner: includeBanners ? DEFAULT_BANNERS : undefined, + banner: banners, ...overrideEsbuildOptions, })