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

Support ESM configs and explicit .cjs and .mjs config extensions #746

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ temp
LICENSE
CHANGELOG.*
SECURITY.md
*.api.md
11 changes: 11 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@
"sourceMaps": true,
"outputCapture": "std",
"console": "integratedTerminal"
},
{
"name": "Debug example-lib build",
"request": "launch",
"type": "node",
"runtimeExecutable": "yarn",
"runtimeArgs": ["run", "build"],
"cwd": "${workspaceFolder}/packages/example-lib",
"console": "integratedTerminal",
"outputCapture": "std",
"sourceMaps": true
}
]
}
11 changes: 11 additions & 0 deletions change/change-0c7dafd4-b632-47f2-bacf-b22077ce3c51.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"type": "minor",
"comment": "`nodeExecTask`: add `enableTypeScript: esm` setting to run the task in `ts-node` with ESM support",
"packageName": "just-scripts",
"email": "[email protected]",
"dependentChangeType": "patch"
}
]
}
11 changes: 11 additions & 0 deletions change/change-3e7ef03c-6140-4b48-b824-f7303ca53d62.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"type": "patch",
"comment": "Detect if already running in ts-node or tsx and skip call to ts-node register",
"packageName": "just-task",
"email": "[email protected]",
"dependentChangeType": "patch"
}
]
}
11 changes: 11 additions & 0 deletions change/change-48ef8505-5b31-4b60-bfe2-53e1433985cd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"type": "minor",
"comment": "Compile just with TS 4.7 and `module`/`moduleResolution` `\"Node16\"`",
"packageName": "just-scripts",
"email": "[email protected]",
"dependentChangeType": "patch"
}
]
}
11 changes: 11 additions & 0 deletions change/change-9ea30187-bf7e-4cd9-ba0a-e1af7e7b3bdc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"type": "minor",
"comment": "For ts-node, use `module`/`moduleResolution` `\"Node16\"` if the local TS version supports it",
"packageName": "just-task",
"email": "[email protected]",
"dependentChangeType": "patch"
}
]
}
11 changes: 11 additions & 0 deletions change/change-9eb2d5fb-3a3f-4714-8fd7-5bac2166d20d.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"type": "minor",
"comment": "Partial support for ESM packages: load `just.config.js` as ESM if package has `type: \"module\"`, and support explicit `.cjs`, `.mjs`, and `.cts` extensions. (`.ts` configs in packages with `type: \"module\"` are not supported due to limitations with `ts-node` `register()`.)",
"packageName": "just-task",
"email": "[email protected]",
"dependentChangeType": "patch"
}
]
}
11 changes: 11 additions & 0 deletions change/change-e400c7a0-47ba-4cf5-b712-508ad7481673.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"type": "minor",
"comment": "Compile just with TS 4.7 and `module`/`moduleResolution` `\"Node16\"`",
"packageName": "just-task",
"email": "[email protected]",
"dependentChangeType": "patch"
}
]
}
14 changes: 13 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"prettier": "^2.8.4",
"syncpack": "^9.0.0",
"ts-jest": "^29.0.5",
"typescript": "~4.3.5",
"typescript": "~4.7.0",
"vuepress": "^1.9.9",
"vuepress-plugin-mermaidjs": "^1.9.1",
"workspace-tools": "^0.35.2"
Expand All @@ -74,6 +74,18 @@
"dependencyTypes": [
"dev",
"prod"
],
"versionGroups": [
{
"label": "ts-node (testing with multiple versions)",
"dependencies": [
"ts-node"
],
"packages": [
"**"
],
"isIgnored": true
}
]
},
"workspaces": {
Expand Down
3 changes: 3 additions & 0 deletions packages/example-lib-esm-ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This package has `"type": "module"` in its `package.json`. If the just config was `just.config.ts`, this would be implicitly considered as ESM, and loading TS files as ESM isn't supported with `ts-node` `register()` (it must be configured with a `--loader` option when the Node process is created). So the package has to use `just.config.cts` instead.

The just config also defines a `nodeExecTask` which must be handled as ESM. The custom task file can be a normal `.ts` which is treated as ESM because `enableTypeScript: 'esm'` is set, and the task is run in a separate Node process where the `--loader` can be used.
14 changes: 14 additions & 0 deletions packages/example-lib-esm-ts/just.config.cts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { nodeExecTask, tscTask, task, parallel } from 'just-scripts';

task('typescript', tscTask({}));

task(
'customNodeTask',
nodeExecTask({
enableTypeScript: 'esm',
transpileOnly: true,
args: ['./tasks/customTask.ts'],
}),
);

task('build', parallel('customNodeTask', 'typescript'));
14 changes: 14 additions & 0 deletions packages/example-lib-esm-ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "example-lib-esm-ts",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "just-scripts build"
},
"license": "MIT",
"devDependencies": {
"just-scripts": ">=2.2.3 <3.0.0",
"ts-node": "^10.0.0"
}
}
1 change: 1 addition & 0 deletions packages/example-lib-esm-ts/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const a = 5;
7 changes: 7 additions & 0 deletions packages/example-lib-esm-ts/tasks/customTask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as path from 'path';
import * as fs from 'fs';
import { fileURLToPath } from 'url';

const dirname = path.dirname(fileURLToPath(import.meta.url));

export const packageJson = fs.readFileSync(path.resolve(dirname, '../package.json'), 'utf-8');
15 changes: 15 additions & 0 deletions packages/example-lib-esm-ts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"declarationMap": true,
"outDir": "lib",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": []
},
"include": ["src"]
}
1 change: 1 addition & 0 deletions packages/example-lib-esm-tsnode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This package has `"type": "module"` in its `package.json`. The config is a `.ts` which will be treated as ESM, and Just can't handle that directly (see [`example-lib-esm-ts` readme](../example-lib-esm-ts/README.md)). So the package's `"build"` script wraps the `just-scripts` binary with the transpiler: `ts-node-esm node_modules/.bin/just-scripts build`.
14 changes: 14 additions & 0 deletions packages/example-lib-esm-tsnode/just.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { nodeExecTask, tscTask, task, parallel } from 'just-scripts';

task('typescript', tscTask({}));

task(
'customNodeTask',
nodeExecTask({
enableTypeScript: 'esm',
transpileOnly: true,
args: ['./tasks/customTask.ts'],
}),
);

task('build', parallel('customNodeTask', 'typescript'));
14 changes: 14 additions & 0 deletions packages/example-lib-esm-tsnode/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "example-lib-esm-tsnode",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "ts-node-esm node_modules/.bin/just-scripts build"
},
"license": "MIT",
"devDependencies": {
"just-scripts": ">=2.2.3 <3.0.0",
"ts-node": "^10.0.0"
}
}
1 change: 1 addition & 0 deletions packages/example-lib-esm-tsnode/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const a = 5;
7 changes: 7 additions & 0 deletions packages/example-lib-esm-tsnode/tasks/customTask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as path from 'path';
import * as fs from 'fs';
import { fileURLToPath } from 'url';

const dirname = path.dirname(fileURLToPath(import.meta.url));

export const packageJson = fs.readFileSync(path.resolve(dirname, '../package.json'), 'utf-8');
15 changes: 15 additions & 0 deletions packages/example-lib-esm-tsnode/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"declarationMap": true,
"outDir": "lib",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": []
},
"include": ["src"]
}
3 changes: 3 additions & 0 deletions packages/example-lib-esm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This package has `"type": "module"` in its `package.json`, so its `just.config.js` must be loaded as ESM.

The just config also defines a `nodeExecTask` which must be handled as ESM.
7 changes: 7 additions & 0 deletions packages/example-lib-esm/just.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { nodeExecTask, tscTask, task, parallel } from 'just-scripts';

task('typescript', tscTask({}));

task('customNodeTask', nodeExecTask({ args: ['./tasks/customTask.js'] }));

task('build', parallel('customNodeTask', 'typescript'));
14 changes: 14 additions & 0 deletions packages/example-lib-esm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "example-lib-esm",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "just-scripts build"
},
"license": "MIT",
"devDependencies": {
"just-scripts": ">=2.2.3 <3.0.0",
"ts-node": "^9.1.1"
}
}
1 change: 1 addition & 0 deletions packages/example-lib-esm/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const a = 5;
7 changes: 7 additions & 0 deletions packages/example-lib-esm/tasks/customTask.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as path from 'path';
import * as fs from 'fs';
import { fileURLToPath } from 'url';

const dirname = path.dirname(fileURLToPath(import.meta.url));

export const packageJson = fs.readFileSync(path.resolve(dirname, '../package.json'), 'utf-8');
15 changes: 15 additions & 0 deletions packages/example-lib-esm/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"declarationMap": true,
"outDir": "lib",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": []
},
"include": ["src"]
}
9 changes: 8 additions & 1 deletion packages/example-lib/just.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ import { nodeExecTask, tscTask, task, parallel, watch } from 'just-scripts';
task('typescript', tscTask({}));
task('typescript:watch', tscTask({ watch: true }));

task('customNodeTask', nodeExecTask({ enableTypeScript: true, args: ['./tasks/customTask.ts'] }));
task(
'customNodeTask',
nodeExecTask({
enableTypeScript: true,
transpileOnly: true,
args: ['./tasks/customTask.ts'],
}),
);

task('build', parallel('customNodeTask', 'typescript'));
task('watch', parallel('typescript:watch'));
Expand Down
7 changes: 0 additions & 7 deletions packages/example-lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,10 @@
"name": "example-lib",
"private": true,
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "just-scripts build"
},
"keywords": [],
"author": "",
"license": "MIT",
"engines": {
"node": ">=14"
},
"devDependencies": {
"just-scripts": ">=2.2.3 <3.0.0",
"ts-node": "^9.1.1"
Expand Down
4 changes: 2 additions & 2 deletions packages/just-scripts/etc/just-scripts.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ export function nodeExecTask(options: NodeExecTaskOptions): TaskFunction;
// @public (undocumented)
export interface NodeExecTaskOptions {
args?: string[];
enableTypeScript?: boolean;
enableTypeScript?: boolean | 'esm';
env?: NodeJS.ProcessEnv;
spawnOptions?: SpawnOptions;
transpileOnly?: boolean;
Expand Down Expand Up @@ -445,7 +445,7 @@ export interface TsLoaderOptions {
}

// @public (undocumented)
export const tsOverlay: (overlayOptions?: TsOverlayOptions | undefined) => Partial<Configuration>;
export const tsOverlay: (overlayOptions?: TsOverlayOptions) => Partial<Configuration>;

// @public (undocumented)
export interface TsOverlayOptions {
Expand Down
24 changes: 13 additions & 11 deletions packages/just-scripts/src/tasks/nodeExecTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ export interface NodeExecTaskOptions {
env?: NodeJS.ProcessEnv;

/**
* Should this nodeExec task be using something like ts-node to execute the binary
* Whether this nodeExec task should use ts-node to execute the binary.
* If set to `esm`, it will use `ts-node/esm` instead of `ts-node/register`.
*/
enableTypeScript?: boolean;
enableTypeScript?: boolean | 'esm';

/**
* The tsconfig file to pass to ts-node for Typescript config
Expand All @@ -40,21 +41,22 @@ export interface NodeExecTaskOptions {
export function nodeExecTask(options: NodeExecTaskOptions): TaskFunction {
return function () {
const { spawnOptions, enableTypeScript, tsconfig, transpileOnly } = options;
const args = [...(options.args || [])];
const env = { ...options.env };

const tsNodeRegister = resolveCwd('ts-node/register');
const esm = enableTypeScript === 'esm';
const tsNodeHelper = resolveCwd(esm ? 'ts-node/esm.mjs' : 'ts-node/register');
const nodeExecPath = process.execPath;

if (enableTypeScript && tsNodeRegister) {
options.args = options.args || [];
options.args.unshift(tsNodeRegister);
options.args.unshift('-r');
if (enableTypeScript && tsNodeHelper) {
args.unshift(esm ? '--loader' : '-r', tsNodeHelper);
Object.assign(env, getTsNodeEnv(tsconfig, transpileOnly, esm));

options.env = { ...options.env, ...getTsNodeEnv(tsconfig, transpileOnly) };
logger.info('Executing [TS]: ' + [nodeExecPath, ...(options.args || [])].join(' '));
logger.info('Executing [TS]: ' + [nodeExecPath, ...args].join(' '));
} else {
logger.info('Executing: ' + [nodeExecPath, ...(options.args || [])].join(' '));
logger.info('Executing: ' + [nodeExecPath, ...args].join(' '));
}

return spawn(nodeExecPath, options.args, { stdio: 'inherit', env: options.env, ...spawnOptions });
return spawn(nodeExecPath, args, { stdio: 'inherit', env, ...spawnOptions });
};
}
Loading
Loading