feat(js): add generateExportsField and additionalEntryPoints options to update exports field when building with tsc/swc (#18319)
This commit is contained in:
parent
90e4e7e7de
commit
d63d3573c4
@ -14,18 +14,34 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The name of the main entry-point file.",
|
"description": "The name of the main entry-point file.",
|
||||||
"x-completion-type": "file",
|
"x-completion-type": "file",
|
||||||
"x-completion-glob": "main@(.js|.ts|.tsx)"
|
"x-completion-glob": "main@(.js|.ts|.tsx)",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"generateExportsField": {
|
||||||
|
"type": "boolean",
|
||||||
|
"alias": "exports",
|
||||||
|
"description": "Update the output package.json file's 'exports' field. This field is used by Node and bundles.",
|
||||||
|
"x-priority": "important",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"additionalEntryPoints": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Additional entry-points to add to exports field in the package.json file.",
|
||||||
|
"items": { "type": "string" },
|
||||||
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"outputPath": {
|
"outputPath": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The output path of the generated files.",
|
"description": "The output path of the generated files.",
|
||||||
"x-completion-type": "directory"
|
"x-completion-type": "directory",
|
||||||
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"tsConfig": {
|
"tsConfig": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The path to the Typescript configuration file.",
|
"description": "The path to the Typescript configuration file.",
|
||||||
"x-completion-type": "file",
|
"x-completion-type": "file",
|
||||||
"x-completion-glob": "tsconfig.*.json"
|
"x-completion-glob": "tsconfig.*.json",
|
||||||
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"swcrc": {
|
"swcrc": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -14,7 +14,21 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The name of the main entry-point file.",
|
"description": "The name of the main entry-point file.",
|
||||||
"x-completion-type": "file",
|
"x-completion-type": "file",
|
||||||
"x-completion-glob": "main@(.js|.ts|.jsx|.tsx)"
|
"x-completion-glob": "main@(.js|.ts|.jsx|.tsx)",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"generateExportsField": {
|
||||||
|
"type": "boolean",
|
||||||
|
"alias": "exports",
|
||||||
|
"description": "Update the output package.json file's 'exports' field. This field is used by Node and bundles.",
|
||||||
|
"default": false,
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"additionalEntryPoints": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Additional entry-points to add to exports field in the package.json file.",
|
||||||
|
"items": { "type": "string" },
|
||||||
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"rootDir": {
|
"rootDir": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -23,13 +37,15 @@
|
|||||||
"outputPath": {
|
"outputPath": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The output path of the generated files.",
|
"description": "The output path of the generated files.",
|
||||||
"x-completion-type": "directory"
|
"x-completion-type": "directory",
|
||||||
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"tsConfig": {
|
"tsConfig": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The path to the Typescript configuration file.",
|
"description": "The path to the Typescript configuration file.",
|
||||||
"x-completion-type": "file",
|
"x-completion-type": "file",
|
||||||
"x-completion-glob": "tsconfig.*.json"
|
"x-completion-glob": "tsconfig.*.json",
|
||||||
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"assets": {
|
"assets": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
checkFilesExist,
|
updateJson,
|
||||||
|
updateProjectConfig,
|
||||||
cleanupProject,
|
cleanupProject,
|
||||||
newProject,
|
newProject,
|
||||||
runCLI,
|
runCLI,
|
||||||
@ -8,6 +9,8 @@ import {
|
|||||||
createFile,
|
createFile,
|
||||||
uniq,
|
uniq,
|
||||||
getPackageManagerCommand,
|
getPackageManagerCommand,
|
||||||
|
readJson,
|
||||||
|
updateFile,
|
||||||
} from '@nx/e2e/utils';
|
} from '@nx/e2e/utils';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { ensureDirSync } from 'fs-extra';
|
import { ensureDirSync } from 'fs-extra';
|
||||||
@ -123,17 +126,74 @@ describe('bundling libs', () => {
|
|||||||
it('should support tsc and swc for building libs', () => {
|
it('should support tsc and swc for building libs', () => {
|
||||||
const tscLib = uniq('tsclib');
|
const tscLib = uniq('tsclib');
|
||||||
const swcLib = uniq('swclib');
|
const swcLib = uniq('swclib');
|
||||||
|
const tscEsmLib = uniq('tscesmlib');
|
||||||
|
const swcEsmLib = uniq('swcesmlib');
|
||||||
|
|
||||||
runCLI(`generate @nx/js:lib ${tscLib} --bundler=tsc --no-interactive`);
|
runCLI(`generate @nx/js:lib ${tscLib} --bundler=tsc --no-interactive`);
|
||||||
runCLI(`generate @nx/js:lib ${swcLib} --bundler=swc --no-interactive`);
|
runCLI(`generate @nx/js:lib ${swcLib} --bundler=swc --no-interactive`);
|
||||||
|
runCLI(`generate @nx/js:lib ${tscEsmLib} --bundler=tsc --no-interactive`);
|
||||||
|
runCLI(`generate @nx/js:lib ${swcEsmLib} --bundler=swc --no-interactive`);
|
||||||
|
|
||||||
runCLI(`build ${tscLib}`);
|
// Change module format to ESM
|
||||||
runCLI(`build ${swcLib}`);
|
updateJson(`libs/${tscEsmLib}/tsconfig.json`, (json) => {
|
||||||
|
json.compilerOptions.module = 'esnext';
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
updateJson(`libs/${swcEsmLib}/.swcrc`, (json) => {
|
||||||
|
json.module.type = 'es6';
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
// Node ESM requires file extensions in imports so must add them before building
|
||||||
|
updateFile(
|
||||||
|
`libs/${tscEsmLib}/src/index.ts`,
|
||||||
|
`export * from './lib/${tscEsmLib}.js';`
|
||||||
|
);
|
||||||
|
updateFile(
|
||||||
|
`libs/${swcEsmLib}/src/index.ts`,
|
||||||
|
`export * from './lib/${swcEsmLib}.js';`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add additional entry points for `exports` field
|
||||||
|
updateProjectConfig(tscLib, (json) => {
|
||||||
|
json.targets.build.options.additionalEntryPoints = [
|
||||||
|
`libs/${tscLib}/src/foo/*.ts`,
|
||||||
|
];
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
updateFile(`libs/${tscLib}/src/foo/bar.ts`, `export const bar = 'bar';`);
|
||||||
|
updateFile(`libs/${tscLib}/src/foo/faz.ts`, `export const faz = 'faz';`);
|
||||||
|
updateProjectConfig(swcLib, (json) => {
|
||||||
|
json.targets.build.options.additionalEntryPoints = [
|
||||||
|
`libs/${swcLib}/src/foo/*.ts`,
|
||||||
|
];
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
updateFile(`libs/${swcLib}/src/foo/bar.ts`, `export const bar = 'bar';`);
|
||||||
|
updateFile(`libs/${swcLib}/src/foo/faz.ts`, `export const faz = 'faz';`);
|
||||||
|
|
||||||
|
runCLI(`build ${tscLib} --generateExportsField`);
|
||||||
|
runCLI(`build ${swcLib} --generateExportsField`);
|
||||||
|
runCLI(`build ${tscEsmLib} --generateExportsField`);
|
||||||
|
runCLI(`build ${swcEsmLib} --generateExportsField`);
|
||||||
|
|
||||||
|
expect(readJson(`dist/libs/${tscLib}/package.json`).exports).toEqual({
|
||||||
|
'./package.json': './package.json',
|
||||||
|
'.': './src/index.js',
|
||||||
|
'./foo/bar': './src/foo/bar.js',
|
||||||
|
'./foo/faz': './src/foo/faz.js',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(readJson(`dist/libs/${swcLib}/package.json`).exports).toEqual({
|
||||||
|
'./package.json': './package.json',
|
||||||
|
'.': './src/index.js',
|
||||||
|
'./foo/bar': './src/foo/bar.js',
|
||||||
|
'./foo/faz': './src/foo/faz.js',
|
||||||
|
});
|
||||||
|
|
||||||
const pmc = getPackageManagerCommand();
|
const pmc = getPackageManagerCommand();
|
||||||
let output: string;
|
let output: string;
|
||||||
|
|
||||||
// Make sure outputs in commonjs project
|
// Make sure CJS output is correct
|
||||||
createFile(
|
createFile(
|
||||||
'test-cjs/package.json',
|
'test-cjs/package.json',
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
@ -155,8 +215,13 @@ describe('bundling libs', () => {
|
|||||||
`
|
`
|
||||||
const { ${tscLib} } = require('@proj/${tscLib}');
|
const { ${tscLib} } = require('@proj/${tscLib}');
|
||||||
const { ${swcLib} } = require('@proj/${swcLib}');
|
const { ${swcLib} } = require('@proj/${swcLib}');
|
||||||
|
// additional entry-points
|
||||||
|
const { bar } = require('@proj/${tscLib}/foo/bar');
|
||||||
|
const { faz } = require('@proj/${swcLib}/foo/faz');
|
||||||
console.log(${tscLib}());
|
console.log(${tscLib}());
|
||||||
console.log(${swcLib}());
|
console.log(${swcLib}());
|
||||||
|
console.log(bar);
|
||||||
|
console.log(faz);
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
runCommand(pmc.install, {
|
runCommand(pmc.install, {
|
||||||
@ -167,5 +232,42 @@ describe('bundling libs', () => {
|
|||||||
});
|
});
|
||||||
expect(output).toContain(tscLib);
|
expect(output).toContain(tscLib);
|
||||||
expect(output).toContain(swcLib);
|
expect(output).toContain(swcLib);
|
||||||
|
expect(output).toContain('bar');
|
||||||
|
expect(output).toContain('faz');
|
||||||
|
|
||||||
|
// Make sure ESM output is correct
|
||||||
|
createFile(
|
||||||
|
'test-esm/package.json',
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
name: 'test-esm',
|
||||||
|
private: true,
|
||||||
|
type: 'module',
|
||||||
|
dependencies: {
|
||||||
|
[`@proj/${tscEsmLib}`]: `file:../dist/libs/${tscEsmLib}`,
|
||||||
|
[`@proj/${swcEsmLib}`]: `file:../dist/libs/${swcEsmLib}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
createFile(
|
||||||
|
'test-esm/index.js',
|
||||||
|
`
|
||||||
|
import { ${tscEsmLib} } from '@proj/${tscEsmLib}';
|
||||||
|
import { ${swcEsmLib} } from '@proj/${swcEsmLib}';
|
||||||
|
console.log(${tscEsmLib}());
|
||||||
|
console.log(${swcEsmLib}());
|
||||||
|
`
|
||||||
|
);
|
||||||
|
runCommand(pmc.install, {
|
||||||
|
cwd: join(tmpProjPath(), 'test-esm'),
|
||||||
|
});
|
||||||
|
output = runCommand('node index.js', {
|
||||||
|
cwd: join(tmpProjPath(), 'test-esm'),
|
||||||
|
});
|
||||||
|
expect(output).toContain(tscEsmLib);
|
||||||
|
expect(output).toContain(swcEsmLib);
|
||||||
}, 500_000);
|
}, 500_000);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import { InspectType, NodeExecutorOptions } from './schema';
|
|||||||
import { calculateProjectBuildableDependencies } from '../../utils/buildable-libs-utils';
|
import { calculateProjectBuildableDependencies } from '../../utils/buildable-libs-utils';
|
||||||
import { killTree } from './lib/kill-tree';
|
import { killTree } from './lib/kill-tree';
|
||||||
import { fileExists } from 'nx/src/utils/fileutils';
|
import { fileExists } from 'nx/src/utils/fileutils';
|
||||||
import { getMainFileDirRelativeToProjectRoot } from '../../utils/get-main-file-dir';
|
import { getRelativeDirectoryToProjectRoot } from '../../utils/get-main-file-dir';
|
||||||
|
|
||||||
interface ActiveTask {
|
interface ActiveTask {
|
||||||
id: string;
|
id: string;
|
||||||
@ -379,10 +379,7 @@ function getFileToRun(
|
|||||||
buildTargetExecutor === '@nx/js:swc'
|
buildTargetExecutor === '@nx/js:swc'
|
||||||
) {
|
) {
|
||||||
outputFileName = path.join(
|
outputFileName = path.join(
|
||||||
getMainFileDirRelativeToProjectRoot(
|
getRelativeDirectoryToProjectRoot(buildOptions.main, project.data.root),
|
||||||
buildOptions.main,
|
|
||||||
project.data.root
|
|
||||||
),
|
|
||||||
fileName
|
fileName
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -11,18 +11,36 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The name of the main entry-point file.",
|
"description": "The name of the main entry-point file.",
|
||||||
"x-completion-type": "file",
|
"x-completion-type": "file",
|
||||||
"x-completion-glob": "main@(.js|.ts|.tsx)"
|
"x-completion-glob": "main@(.js|.ts|.tsx)",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"generateExportsField": {
|
||||||
|
"type": "boolean",
|
||||||
|
"alias": "exports",
|
||||||
|
"description": "Update the output package.json file's 'exports' field. This field is used by Node and bundles.",
|
||||||
|
"x-priority": "important",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"additionalEntryPoints": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Additional entry-points to add to exports field in the package.json file.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"outputPath": {
|
"outputPath": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The output path of the generated files.",
|
"description": "The output path of the generated files.",
|
||||||
"x-completion-type": "directory"
|
"x-completion-type": "directory",
|
||||||
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"tsConfig": {
|
"tsConfig": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The path to the Typescript configuration file.",
|
"description": "The path to the Typescript configuration file.",
|
||||||
"x-completion-type": "file",
|
"x-completion-type": "file",
|
||||||
"x-completion-glob": "tsconfig.*.json"
|
"x-completion-glob": "tsconfig.*.json",
|
||||||
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"swcrc": {
|
"swcrc": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { ExecutorContext } from '@nx/devkit';
|
import { ExecutorContext, readJsonFile } from '@nx/devkit';
|
||||||
import { assetGlobsToFiles, FileInputOutput } from '../../utils/assets/assets';
|
import { assetGlobsToFiles, FileInputOutput } from '../../utils/assets/assets';
|
||||||
import { removeSync } from 'fs-extra';
|
import { removeSync } from 'fs-extra';
|
||||||
|
import { sync as globSync } from 'fast-glob';
|
||||||
import { dirname, join, relative, resolve } from 'path';
|
import { dirname, join, relative, resolve } from 'path';
|
||||||
import { copyAssets } from '../../utils/assets';
|
import { copyAssets } from '../../utils/assets';
|
||||||
import { checkDependencies } from '../../utils/check-dependencies';
|
import { checkDependencies } from '../../utils/check-dependencies';
|
||||||
@ -135,6 +136,13 @@ export async function* swcExecutor(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function determineModuleFormatFromSwcrc(
|
||||||
|
absolutePathToSwcrc: string
|
||||||
|
): 'cjs' | 'esm' {
|
||||||
|
const swcrc = readJsonFile(absolutePathToSwcrc);
|
||||||
|
return swcrc.module?.type?.startsWith('es') ? 'esm' : 'cjs';
|
||||||
|
}
|
||||||
|
|
||||||
if (options.watch) {
|
if (options.watch) {
|
||||||
let disposeFn: () => void;
|
let disposeFn: () => void;
|
||||||
process.on('SIGINT', () => disposeFn());
|
process.on('SIGINT', () => disposeFn());
|
||||||
@ -145,7 +153,13 @@ export async function* swcExecutor(
|
|||||||
const packageJsonResult = await copyPackageJson(
|
const packageJsonResult = await copyPackageJson(
|
||||||
{
|
{
|
||||||
...options,
|
...options,
|
||||||
skipTypings: !options.skipTypeCheck,
|
additionalEntryPoints: createEntryPoints(options, context),
|
||||||
|
format: [
|
||||||
|
determineModuleFormatFromSwcrc(options.swcCliOptions.swcrcPath),
|
||||||
|
],
|
||||||
|
// As long as d.ts files match their .js counterparts, we don't need to emit them.
|
||||||
|
// TSC can match them correctly based on file names.
|
||||||
|
skipTypings: true,
|
||||||
},
|
},
|
||||||
context
|
context
|
||||||
);
|
);
|
||||||
@ -161,8 +175,13 @@ export async function* swcExecutor(
|
|||||||
await copyPackageJson(
|
await copyPackageJson(
|
||||||
{
|
{
|
||||||
...options,
|
...options,
|
||||||
generateExportsField: true,
|
additionalEntryPoints: createEntryPoints(options, context),
|
||||||
skipTypings: !options.skipTypeCheck,
|
format: [
|
||||||
|
determineModuleFormatFromSwcrc(options.swcCliOptions.swcrcPath),
|
||||||
|
],
|
||||||
|
// As long as d.ts files match their .js counterparts, we don't need to emit them.
|
||||||
|
// TSC can match them correctly based on file names.
|
||||||
|
skipTypings: true,
|
||||||
extraDependencies: swcHelperDependency ? [swcHelperDependency] : [],
|
extraDependencies: swcHelperDependency ? [swcHelperDependency] : [],
|
||||||
},
|
},
|
||||||
context
|
context
|
||||||
@ -183,4 +202,14 @@ function removeTmpSwcrc(swcrcPath: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createEntryPoints(
|
||||||
|
options: { additionalEntryPoints?: string[] },
|
||||||
|
context: ExecutorContext
|
||||||
|
): string[] {
|
||||||
|
if (!options.additionalEntryPoints?.length) return [];
|
||||||
|
return globSync(options.additionalEntryPoints, {
|
||||||
|
cwd: context.root,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default swcExecutor;
|
export default swcExecutor;
|
||||||
|
|||||||
@ -10,7 +10,23 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The name of the main entry-point file.",
|
"description": "The name of the main entry-point file.",
|
||||||
"x-completion-type": "file",
|
"x-completion-type": "file",
|
||||||
"x-completion-glob": "main@(.js|.ts|.jsx|.tsx)"
|
"x-completion-glob": "main@(.js|.ts|.jsx|.tsx)",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"generateExportsField": {
|
||||||
|
"type": "boolean",
|
||||||
|
"alias": "exports",
|
||||||
|
"description": "Update the output package.json file's 'exports' field. This field is used by Node and bundles.",
|
||||||
|
"default": false,
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"additionalEntryPoints": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Additional entry-points to add to exports field in the package.json file.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"rootDir": {
|
"rootDir": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -19,13 +35,15 @@
|
|||||||
"outputPath": {
|
"outputPath": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The output path of the generated files.",
|
"description": "The output path of the generated files.",
|
||||||
"x-completion-type": "directory"
|
"x-completion-type": "directory",
|
||||||
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"tsConfig": {
|
"tsConfig": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The path to the Typescript configuration file.",
|
"description": "The path to the Typescript configuration file.",
|
||||||
"x-completion-type": "file",
|
"x-completion-type": "file",
|
||||||
"x-completion-glob": "tsconfig.*.json"
|
"x-completion-glob": "tsconfig.*.json",
|
||||||
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"assets": {
|
"assets": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
|||||||
@ -4,6 +4,10 @@ import type { BatchExecutorTaskResult } from 'nx/src/config/misc-interfaces';
|
|||||||
import { getLastValueFromAsyncIterableIterator } from 'nx/src/utils/async-iterator';
|
import { getLastValueFromAsyncIterableIterator } from 'nx/src/utils/async-iterator';
|
||||||
import { updatePackageJson } from '../../utils/package-json/update-package-json';
|
import { updatePackageJson } from '../../utils/package-json/update-package-json';
|
||||||
import type { ExecutorOptions } from '../../utils/schema';
|
import type { ExecutorOptions } from '../../utils/schema';
|
||||||
|
import {
|
||||||
|
createEntryPoints,
|
||||||
|
determineModuleFormatFromTsConfig,
|
||||||
|
} from './tsc.impl';
|
||||||
import {
|
import {
|
||||||
TypescripCompilationLogger,
|
TypescripCompilationLogger,
|
||||||
TypescriptCompilationResult,
|
TypescriptCompilationResult,
|
||||||
@ -81,7 +85,14 @@ export async function* tscBatchExecutor(
|
|||||||
const taskInfo = tsConfigTaskInfoMap[tsConfig];
|
const taskInfo = tsConfigTaskInfoMap[tsConfig];
|
||||||
taskInfo.assetsHandler.processAllAssetsOnceSync();
|
taskInfo.assetsHandler.processAllAssetsOnceSync();
|
||||||
updatePackageJson(
|
updatePackageJson(
|
||||||
taskInfo.options,
|
{
|
||||||
|
...taskInfo.options,
|
||||||
|
additionalEntryPoints: createEntryPoints(taskInfo.options, context),
|
||||||
|
format: [determineModuleFormatFromTsConfig(tsConfig)],
|
||||||
|
// As long as d.ts files match their .js counterparts, we don't need to emit them.
|
||||||
|
// TSC can match them correctly based on file names.
|
||||||
|
skipTypings: true,
|
||||||
|
},
|
||||||
taskInfo.context,
|
taskInfo.context,
|
||||||
taskInfo.projectGraphNode,
|
taskInfo.projectGraphNode,
|
||||||
taskInfo.buildableProjectNodeDependencies
|
taskInfo.buildableProjectNodeDependencies
|
||||||
@ -114,7 +125,14 @@ export async function* tscBatchExecutor(
|
|||||||
(changedTaskInfos: TaskInfo[]) => {
|
(changedTaskInfos: TaskInfo[]) => {
|
||||||
for (const t of changedTaskInfos) {
|
for (const t of changedTaskInfos) {
|
||||||
updatePackageJson(
|
updatePackageJson(
|
||||||
t.options,
|
{
|
||||||
|
...t.options,
|
||||||
|
additionalEntryPoints: createEntryPoints(t.options, context),
|
||||||
|
format: [determineModuleFormatFromTsConfig(t.options.tsConfig)],
|
||||||
|
// As long as d.ts files match their .js counterparts, we don't need to emit them.
|
||||||
|
// TSC can match them correctly based on file names.
|
||||||
|
skipTypings: true,
|
||||||
|
},
|
||||||
t.context,
|
t.context,
|
||||||
t.projectGraphNode,
|
t.projectGraphNode,
|
||||||
t.buildableProjectNodeDependencies
|
t.buildableProjectNodeDependencies
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import * as ts from 'typescript';
|
||||||
|
import { sync as globSync } from 'fast-glob';
|
||||||
import { ExecutorContext } from '@nx/devkit';
|
import { ExecutorContext } from '@nx/devkit';
|
||||||
import type { TypeScriptCompilationOptions } from '@nx/workspace/src/utilities/typescript/compilation';
|
import type { TypeScriptCompilationOptions } from '@nx/workspace/src/utilities/typescript/compilation';
|
||||||
import { CopyAssetsHandler } from '../../utils/assets/copy-assets-handler';
|
import { CopyAssetsHandler } from '../../utils/assets/copy-assets-handler';
|
||||||
@ -16,6 +18,23 @@ import { ExecutorOptions, NormalizedExecutorOptions } from '../../utils/schema';
|
|||||||
import { compileTypeScriptFiles } from '../../utils/typescript/compile-typescript-files';
|
import { compileTypeScriptFiles } from '../../utils/typescript/compile-typescript-files';
|
||||||
import { watchForSingleFileChanges } from '../../utils/watch-for-single-file-changes';
|
import { watchForSingleFileChanges } from '../../utils/watch-for-single-file-changes';
|
||||||
import { getCustomTrasformersFactory, normalizeOptions } from './lib';
|
import { getCustomTrasformersFactory, normalizeOptions } from './lib';
|
||||||
|
import { readTsConfig } from '../../utils/typescript/ts-config';
|
||||||
|
|
||||||
|
export function determineModuleFormatFromTsConfig(
|
||||||
|
absolutePathToTsConfig: string
|
||||||
|
): 'cjs' | 'esm' {
|
||||||
|
const tsConfig = readTsConfig(absolutePathToTsConfig);
|
||||||
|
if (
|
||||||
|
tsConfig.options.module === ts.ModuleKind.ES2015 ||
|
||||||
|
tsConfig.options.module === ts.ModuleKind.ES2020 ||
|
||||||
|
tsConfig.options.module === ts.ModuleKind.ES2022 ||
|
||||||
|
tsConfig.options.module === ts.ModuleKind.ESNext
|
||||||
|
) {
|
||||||
|
return 'esm';
|
||||||
|
} else {
|
||||||
|
return 'cjs';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function createTypeScriptCompilationOptions(
|
export function createTypeScriptCompilationOptions(
|
||||||
normalizedOptions: NormalizedExecutorOptions,
|
normalizedOptions: NormalizedExecutorOptions,
|
||||||
@ -90,7 +109,19 @@ export async function* tscExecutor(
|
|||||||
tsCompilationOptions,
|
tsCompilationOptions,
|
||||||
async () => {
|
async () => {
|
||||||
await assetHandler.processAllAssetsOnce();
|
await assetHandler.processAllAssetsOnce();
|
||||||
updatePackageJson(options, context, target, dependencies);
|
updatePackageJson(
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
additionalEntryPoints: createEntryPoints(options, context),
|
||||||
|
format: [determineModuleFormatFromTsConfig(options.tsConfig)],
|
||||||
|
// As long as d.ts files match their .js counterparts, we don't need to emit them.
|
||||||
|
// TSC can match them correctly based on file names.
|
||||||
|
skipTypings: true,
|
||||||
|
},
|
||||||
|
context,
|
||||||
|
target,
|
||||||
|
dependencies
|
||||||
|
);
|
||||||
postProcessInlinedDependencies(
|
postProcessInlinedDependencies(
|
||||||
tsCompilationOptions.outputPath,
|
tsCompilationOptions.outputPath,
|
||||||
tsCompilationOptions.projectRoot,
|
tsCompilationOptions.projectRoot,
|
||||||
@ -106,7 +137,20 @@ export async function* tscExecutor(
|
|||||||
context.projectName,
|
context.projectName,
|
||||||
options.projectRoot,
|
options.projectRoot,
|
||||||
'package.json',
|
'package.json',
|
||||||
() => updatePackageJson(options, context, target, dependencies)
|
() =>
|
||||||
|
updatePackageJson(
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
additionalEntryPoints: createEntryPoints(options, context),
|
||||||
|
// As long as d.ts files match their .js counterparts, we don't need to emit them.
|
||||||
|
// TSC can match them correctly based on file names.
|
||||||
|
skipTypings: true,
|
||||||
|
format: [determineModuleFormatFromTsConfig(options.tsConfig)],
|
||||||
|
},
|
||||||
|
context,
|
||||||
|
target,
|
||||||
|
dependencies
|
||||||
|
)
|
||||||
);
|
);
|
||||||
const handleTermination = async (exitCode: number) => {
|
const handleTermination = async (exitCode: number) => {
|
||||||
await typescriptCompilation.close();
|
await typescriptCompilation.close();
|
||||||
@ -121,4 +165,14 @@ export async function* tscExecutor(
|
|||||||
return yield* typescriptCompilation.iterator;
|
return yield* typescriptCompilation.iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createEntryPoints(
|
||||||
|
options: { additionalEntryPoints?: string[] },
|
||||||
|
context: ExecutorContext
|
||||||
|
): string[] {
|
||||||
|
if (!options.additionalEntryPoints?.length) return [];
|
||||||
|
return globSync(options.additionalEntryPoints, {
|
||||||
|
cwd: context.root,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default tscExecutor;
|
export default tscExecutor;
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { dirname, relative } from 'path';
|
import { dirname, relative } from 'path';
|
||||||
import { normalizePath } from 'nx/src/utils/path';
|
import { normalizePath } from 'nx/src/utils/path';
|
||||||
|
|
||||||
export function getMainFileDirRelativeToProjectRoot(
|
export function getRelativeDirectoryToProjectRoot(
|
||||||
main: string,
|
file: string,
|
||||||
projectRoot: string
|
projectRoot: string
|
||||||
): string {
|
): string {
|
||||||
const mainFileDir = dirname(main);
|
const dir = dirname(file);
|
||||||
const relativeDir = normalizePath(relative(projectRoot, mainFileDir));
|
const relativeDir = normalizePath(relative(projectRoot, dir));
|
||||||
return relativeDir === '' ? `./` : `./${relativeDir}/`;
|
return relativeDir === '' ? `./` : `./${relativeDir}/`;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,7 @@ describe('getUpdatedPackageJsonContent', () => {
|
|||||||
expect(json).toEqual({
|
expect(json).toEqual({
|
||||||
name: 'test',
|
name: 'test',
|
||||||
main: './src/index.js',
|
main: './src/index.js',
|
||||||
|
type: 'commonjs',
|
||||||
types: './src/index.d.ts',
|
types: './src/index.d.ts',
|
||||||
version: '0.0.1',
|
version: '0.0.1',
|
||||||
});
|
});
|
||||||
@ -99,66 +100,11 @@ describe('getUpdatedPackageJsonContent', () => {
|
|||||||
expect(json).toEqual({
|
expect(json).toEqual({
|
||||||
name: 'test',
|
name: 'test',
|
||||||
main: './src/index.js',
|
main: './src/index.js',
|
||||||
|
type: 'commonjs',
|
||||||
version: '0.0.1',
|
version: '0.0.1',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support generated exports field', () => {
|
|
||||||
const json = getUpdatedPackageJsonContent(
|
|
||||||
{
|
|
||||||
name: 'test',
|
|
||||||
version: '0.0.1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
main: 'proj/src/index.ts',
|
|
||||||
outputPath: 'dist/proj',
|
|
||||||
projectRoot: 'proj',
|
|
||||||
format: ['esm'],
|
|
||||||
generateExportsField: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(json).toEqual({
|
|
||||||
name: 'test',
|
|
||||||
type: 'module',
|
|
||||||
main: './src/index.js',
|
|
||||||
module: './src/index.js',
|
|
||||||
types: './src/index.d.ts',
|
|
||||||
version: '0.0.1',
|
|
||||||
exports: {
|
|
||||||
'.': { import: './src/index.js' },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should support different CJS file extension', () => {
|
|
||||||
const json = getUpdatedPackageJsonContent(
|
|
||||||
{
|
|
||||||
name: 'test',
|
|
||||||
version: '0.0.1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
main: 'proj/src/index.ts',
|
|
||||||
outputPath: 'dist/proj',
|
|
||||||
projectRoot: 'proj',
|
|
||||||
format: ['esm', 'cjs'],
|
|
||||||
outputFileExtensionForCjs: '.cjs',
|
|
||||||
generateExportsField: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(json).toEqual({
|
|
||||||
name: 'test',
|
|
||||||
main: './src/index.cjs',
|
|
||||||
module: './src/index.js',
|
|
||||||
types: './src/index.d.ts',
|
|
||||||
version: '0.0.1',
|
|
||||||
exports: {
|
|
||||||
'.': { require: './src/index.cjs', import: './src/index.js' },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not set types when { skipTypings: true }', () => {
|
it('should not set types when { skipTypings: true }', () => {
|
||||||
const json = getUpdatedPackageJsonContent(
|
const json = getUpdatedPackageJsonContent(
|
||||||
{
|
{
|
||||||
@ -176,68 +122,181 @@ describe('getUpdatedPackageJsonContent', () => {
|
|||||||
expect(json).toEqual({
|
expect(json).toEqual({
|
||||||
name: 'test',
|
name: 'test',
|
||||||
main: './src/index.js',
|
main: './src/index.js',
|
||||||
|
type: 'commonjs',
|
||||||
version: '0.0.1',
|
version: '0.0.1',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support different exports field shape', () => {
|
describe('generateExportsField: true', () => {
|
||||||
// exports: string
|
it('should add ESM exports', () => {
|
||||||
expect(
|
const json = getUpdatedPackageJsonContent(
|
||||||
getUpdatedPackageJsonContent(
|
|
||||||
{
|
{
|
||||||
name: 'test',
|
name: 'test',
|
||||||
version: '0.0.1',
|
version: '0.0.1',
|
||||||
exports: './custom.js',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
main: 'proj/src/index.ts',
|
main: 'proj/src/index.ts',
|
||||||
outputPath: 'dist/proj',
|
outputPath: 'dist/proj',
|
||||||
projectRoot: 'proj',
|
projectRoot: 'proj',
|
||||||
format: ['esm', 'cjs'],
|
format: ['esm'],
|
||||||
outputFileExtensionForCjs: '.cjs',
|
|
||||||
generateExportsField: true,
|
generateExportsField: true,
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
).toEqual({
|
|
||||||
name: 'test',
|
expect(json).toEqual({
|
||||||
main: './src/index.cjs',
|
name: 'test',
|
||||||
module: './src/index.js',
|
type: 'module',
|
||||||
types: './src/index.d.ts',
|
main: './src/index.js',
|
||||||
version: '0.0.1',
|
module: './src/index.js',
|
||||||
exports: './custom.js',
|
types: './src/index.d.ts',
|
||||||
|
version: '0.0.1',
|
||||||
|
exports: {
|
||||||
|
'.': './src/index.js',
|
||||||
|
'./package.json': './package.json',
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// exports: { '.': string }
|
it('should add CJS exports', () => {
|
||||||
expect(
|
const json = getUpdatedPackageJsonContent(
|
||||||
getUpdatedPackageJsonContent(
|
|
||||||
{
|
{
|
||||||
name: 'test',
|
name: 'test',
|
||||||
version: '0.0.1',
|
version: '0.0.1',
|
||||||
exports: {
|
},
|
||||||
'.': './custom.js',
|
{
|
||||||
|
main: 'proj/src/index.ts',
|
||||||
|
outputPath: 'dist/proj',
|
||||||
|
projectRoot: 'proj',
|
||||||
|
format: ['cjs'],
|
||||||
|
outputFileExtensionForCjs: '.cjs',
|
||||||
|
generateExportsField: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(json).toEqual({
|
||||||
|
name: 'test',
|
||||||
|
main: './src/index.cjs',
|
||||||
|
types: './src/index.d.ts',
|
||||||
|
version: '0.0.1',
|
||||||
|
type: 'commonjs',
|
||||||
|
exports: {
|
||||||
|
'.': './src/index.cjs',
|
||||||
|
'./package.json': './package.json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add additional entry-points into package.json', () => {
|
||||||
|
// CJS only
|
||||||
|
expect(
|
||||||
|
getUpdatedPackageJsonContent(
|
||||||
|
{
|
||||||
|
name: 'test',
|
||||||
|
version: '0.0.1',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
main: 'proj/src/index.ts',
|
||||||
|
additionalEntryPoints: [
|
||||||
|
'proj/src/foo.ts',
|
||||||
|
'proj/src/bar.ts',
|
||||||
|
'proj/migrations.json',
|
||||||
|
],
|
||||||
|
outputPath: 'dist/proj',
|
||||||
|
projectRoot: 'proj',
|
||||||
|
format: ['cjs'],
|
||||||
|
generateExportsField: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
name: 'test',
|
||||||
|
main: './src/index.js',
|
||||||
|
type: 'commonjs',
|
||||||
|
types: './src/index.d.ts',
|
||||||
|
version: '0.0.1',
|
||||||
|
exports: {
|
||||||
|
'.': './src/index.js',
|
||||||
|
'./foo': './src/foo.js',
|
||||||
|
'./bar': './src/bar.js',
|
||||||
|
'./package.json': './package.json',
|
||||||
|
'./migrations.json': './migrations.json',
|
||||||
},
|
},
|
||||||
{
|
});
|
||||||
main: 'proj/src/index.ts',
|
|
||||||
outputPath: 'dist/proj',
|
|
||||||
projectRoot: 'proj',
|
|
||||||
format: ['esm', 'cjs'],
|
|
||||||
outputFileExtensionForCjs: '.cjs',
|
|
||||||
generateExportsField: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
).toEqual({
|
|
||||||
name: 'test',
|
|
||||||
main: './src/index.cjs',
|
|
||||||
module: './src/index.js',
|
|
||||||
types: './src/index.d.ts',
|
|
||||||
version: '0.0.1',
|
|
||||||
exports: {
|
|
||||||
'.': './custom.js',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// exports: { './custom': string }
|
// ESM only
|
||||||
|
expect(
|
||||||
|
getUpdatedPackageJsonContent(
|
||||||
|
{
|
||||||
|
name: 'test',
|
||||||
|
version: '0.0.1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
main: 'proj/src/index.ts',
|
||||||
|
additionalEntryPoints: ['proj/src/foo.ts', 'proj/src/bar.ts'],
|
||||||
|
outputPath: 'dist/proj',
|
||||||
|
projectRoot: 'proj',
|
||||||
|
format: ['esm'],
|
||||||
|
generateExportsField: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
name: 'test',
|
||||||
|
type: 'module',
|
||||||
|
main: './src/index.js',
|
||||||
|
module: './src/index.js',
|
||||||
|
types: './src/index.d.ts',
|
||||||
|
version: '0.0.1',
|
||||||
|
exports: {
|
||||||
|
'.': './src/index.js',
|
||||||
|
'./foo': './src/foo.js',
|
||||||
|
'./bar': './src/bar.js',
|
||||||
|
'./package.json': './package.json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dual format
|
||||||
|
expect(
|
||||||
|
getUpdatedPackageJsonContent(
|
||||||
|
{
|
||||||
|
name: 'test',
|
||||||
|
version: '0.0.1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
main: 'proj/src/index.ts',
|
||||||
|
additionalEntryPoints: ['proj/src/foo.ts', 'proj/src/bar.ts'],
|
||||||
|
outputPath: 'dist/proj',
|
||||||
|
projectRoot: 'proj',
|
||||||
|
format: ['cjs', 'esm'],
|
||||||
|
outputFileExtensionForCjs: '.cjs',
|
||||||
|
generateExportsField: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
name: 'test',
|
||||||
|
main: './src/index.cjs',
|
||||||
|
module: './src/index.js',
|
||||||
|
types: './src/index.d.ts',
|
||||||
|
version: '0.0.1',
|
||||||
|
exports: {
|
||||||
|
'.': {
|
||||||
|
import: './src/index.js',
|
||||||
|
default: './src/index.cjs',
|
||||||
|
},
|
||||||
|
'./foo': {
|
||||||
|
import: './src/foo.js',
|
||||||
|
default: './src/foo.cjs',
|
||||||
|
},
|
||||||
|
'./bar': {
|
||||||
|
import: './src/bar.js',
|
||||||
|
default: './src/bar.cjs',
|
||||||
|
},
|
||||||
|
'./package.json': './package.json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support existing exports', () => {
|
||||||
|
// Merge additional exports from user
|
||||||
expect(
|
expect(
|
||||||
getUpdatedPackageJsonContent(
|
getUpdatedPackageJsonContent(
|
||||||
{
|
{
|
||||||
@ -265,8 +324,9 @@ describe('getUpdatedPackageJsonContent', () => {
|
|||||||
exports: {
|
exports: {
|
||||||
'.': {
|
'.': {
|
||||||
import: './src/index.js',
|
import: './src/index.js',
|
||||||
require: './src/index.cjs',
|
default: './src/index.cjs',
|
||||||
},
|
},
|
||||||
|
'./package.json': './package.json',
|
||||||
'./custom': './custom.js',
|
'./custom': './custom.js',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -380,6 +440,7 @@ describe('updatePackageJson', () => {
|
|||||||
{
|
{
|
||||||
"main": "./main.js",
|
"main": "./main.js",
|
||||||
"name": "@org/lib1",
|
"name": "@org/lib1",
|
||||||
|
"type": "commonjs",
|
||||||
"types": "./main.d.ts",
|
"types": "./main.d.ts",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
}
|
}
|
||||||
@ -441,6 +502,7 @@ describe('updatePackageJson', () => {
|
|||||||
},
|
},
|
||||||
"main": "./main.js",
|
"main": "./main.js",
|
||||||
"name": "@org/lib1",
|
"name": "@org/lib1",
|
||||||
|
"type": "commonjs",
|
||||||
"types": "./main.d.ts",
|
"types": "./main.d.ts",
|
||||||
"version": "0.0.3",
|
"version": "0.0.3",
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
} from 'nx/src/plugins/js/lock-file/lock-file';
|
} from 'nx/src/plugins/js/lock-file/lock-file';
|
||||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||||
import { createPackageJson } from 'nx/src/plugins/js/package-json/create-package-json';
|
import { createPackageJson } from 'nx/src/plugins/js/package-json/create-package-json';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ExecutorContext,
|
ExecutorContext,
|
||||||
getOutputsForTargetAndConfiguration,
|
getOutputsForTargetAndConfiguration,
|
||||||
@ -16,25 +17,28 @@ import {
|
|||||||
writeJsonFile,
|
writeJsonFile,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { DependentBuildableProjectNode } from '../buildable-libs-utils';
|
import { DependentBuildableProjectNode } from '../buildable-libs-utils';
|
||||||
import { basename, join, parse } from 'path';
|
import { basename, join, parse, relative } from 'path';
|
||||||
import { writeFileSync } from 'fs-extra';
|
import { writeFileSync } from 'fs-extra';
|
||||||
import { isNpmProject } from 'nx/src/project-graph/operators';
|
import { isNpmProject } from 'nx/src/project-graph/operators';
|
||||||
import { fileExists } from 'nx/src/utils/fileutils';
|
import { fileExists } from 'nx/src/utils/fileutils';
|
||||||
import type { PackageJson } from 'nx/src/utils/package-json';
|
import type { PackageJson } from 'nx/src/utils/package-json';
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
import { readProjectFileMapCache } from 'nx/src/project-graph/nx-deps-cache';
|
import { readProjectFileMapCache } from 'nx/src/project-graph/nx-deps-cache';
|
||||||
|
import * as fastGlob from 'fast-glob';
|
||||||
|
|
||||||
import { getMainFileDirRelativeToProjectRoot } from '../get-main-file-dir';
|
import { getRelativeDirectoryToProjectRoot } from '../get-main-file-dir';
|
||||||
|
|
||||||
export type SupportedFormat = 'cjs' | 'esm';
|
export type SupportedFormat = 'cjs' | 'esm';
|
||||||
|
|
||||||
export interface UpdatePackageJsonOption {
|
export interface UpdatePackageJsonOption {
|
||||||
projectRoot: string;
|
projectRoot: string;
|
||||||
main: string;
|
main: string;
|
||||||
|
additionalEntryPoints?: string[];
|
||||||
format?: SupportedFormat[];
|
format?: SupportedFormat[];
|
||||||
outputPath: string;
|
outputPath: string;
|
||||||
outputFileName?: string;
|
outputFileName?: string;
|
||||||
outputFileExtensionForCjs?: `.${string}`;
|
outputFileExtensionForCjs?: `.${string}`;
|
||||||
|
outputFileExtensionForEsm?: `.${string}`;
|
||||||
skipTypings?: boolean;
|
skipTypings?: boolean;
|
||||||
generateExportsField?: boolean;
|
generateExportsField?: boolean;
|
||||||
excludeLibsInPackageJson?: boolean;
|
excludeLibsInPackageJson?: boolean;
|
||||||
@ -159,6 +163,50 @@ function addMissingDependencies(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Exports {
|
||||||
|
'.': string;
|
||||||
|
|
||||||
|
[name: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getExports(
|
||||||
|
options: Pick<
|
||||||
|
UpdatePackageJsonOption,
|
||||||
|
'main' | 'projectRoot' | 'outputFileName' | 'additionalEntryPoints'
|
||||||
|
> & {
|
||||||
|
fileExt: string;
|
||||||
|
}
|
||||||
|
): Exports {
|
||||||
|
const mainFile = options.outputFileName
|
||||||
|
? options.outputFileName.replace(/\.[tj]s$/, '')
|
||||||
|
: basename(options.main).replace(/\.[tj]s$/, '');
|
||||||
|
const relativeMainFileDir = options.outputFileName
|
||||||
|
? './'
|
||||||
|
: getRelativeDirectoryToProjectRoot(options.main, options.projectRoot);
|
||||||
|
const exports: Exports = {
|
||||||
|
'.': relativeMainFileDir + mainFile + options.fileExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.additionalEntryPoints) {
|
||||||
|
const jsRegex = /\.[jt]sx?$/;
|
||||||
|
|
||||||
|
for (const file of options.additionalEntryPoints) {
|
||||||
|
const { ext: fileExt, name: fileName } = parse(file);
|
||||||
|
const relativeDir = getRelativeDirectoryToProjectRoot(
|
||||||
|
file,
|
||||||
|
options.projectRoot
|
||||||
|
);
|
||||||
|
const sourceFilePath = relativeDir + fileName;
|
||||||
|
const entryFilepath = sourceFilePath.replace(/^\.\/src\//, './');
|
||||||
|
const isJsFile = jsRegex.test(fileExt);
|
||||||
|
exports[isJsFile ? entryFilepath : entryFilepath + fileExt] =
|
||||||
|
sourceFilePath + (isJsFile ? options.fileExt : fileExt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return exports;
|
||||||
|
}
|
||||||
|
|
||||||
export function getUpdatedPackageJsonContent(
|
export function getUpdatedPackageJsonContent(
|
||||||
packageJson: PackageJson,
|
packageJson: PackageJson,
|
||||||
options: UpdatePackageJsonOption
|
options: UpdatePackageJsonOption
|
||||||
@ -167,65 +215,66 @@ export function getUpdatedPackageJsonContent(
|
|||||||
const hasCjsFormat = !options.format || options.format?.includes('cjs');
|
const hasCjsFormat = !options.format || options.format?.includes('cjs');
|
||||||
const hasEsmFormat = options.format?.includes('esm');
|
const hasEsmFormat = options.format?.includes('esm');
|
||||||
|
|
||||||
const mainFile = basename(options.main).replace(/\.[tj]s$/, '');
|
if (options.generateExportsField) {
|
||||||
const relativeMainFileDir = getMainFileDirRelativeToProjectRoot(
|
packageJson.exports =
|
||||||
options.main,
|
typeof packageJson.exports === 'string' ? {} : { ...packageJson.exports };
|
||||||
options.projectRoot
|
packageJson.exports['./package.json'] = './package.json';
|
||||||
);
|
}
|
||||||
const typingsFile = `${relativeMainFileDir}${mainFile}.d.ts`;
|
|
||||||
|
|
||||||
const exports =
|
|
||||||
typeof packageJson.exports === 'string'
|
|
||||||
? packageJson.exports
|
|
||||||
: {
|
|
||||||
'.': {},
|
|
||||||
...packageJson.exports,
|
|
||||||
};
|
|
||||||
|
|
||||||
const mainJsFile =
|
|
||||||
options.outputFileName ?? `${relativeMainFileDir}${mainFile}.js`;
|
|
||||||
|
|
||||||
if (hasEsmFormat) {
|
if (hasEsmFormat) {
|
||||||
// Unofficial field for backwards compat.
|
const esmExports = getExports({
|
||||||
packageJson.module ??= mainJsFile;
|
...options,
|
||||||
|
fileExt: options.outputFileExtensionForEsm ?? '.js',
|
||||||
|
});
|
||||||
|
|
||||||
|
packageJson.module = esmExports['.'];
|
||||||
|
|
||||||
if (!hasCjsFormat) {
|
if (!hasCjsFormat) {
|
||||||
packageJson.type = 'module';
|
packageJson.type = 'module';
|
||||||
packageJson.main ??= mainJsFile;
|
packageJson.main ??= esmExports['.'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof exports !== 'string') {
|
if (options.generateExportsField) {
|
||||||
if (typeof exports['.'] !== 'string') {
|
for (const [exportEntry, filePath] of Object.entries(esmExports)) {
|
||||||
exports['.']['import'] ??= mainJsFile;
|
packageJson.exports[exportEntry] = hasCjsFormat
|
||||||
} else if (!hasCjsFormat) {
|
? { import: filePath }
|
||||||
exports['.'] ??= mainJsFile;
|
: filePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CJS output may have .cjs or .js file extensions.
|
// CJS output may have .cjs or .js file extensions.
|
||||||
// Bundlers like rollup and esbuild supports .cjs for CJS and .js for ESM.
|
// Bundlers like rollup and esbuild supports .cjs for CJS and .js for ESM.
|
||||||
// Bundlers/Compilers like webpack, tsc, swc do not have different file extensions.
|
// Bundlers/Compilers like webpack, tsc, swc do not have different file extensions (unless you use .mts or .cts in source).
|
||||||
if (hasCjsFormat) {
|
if (hasCjsFormat) {
|
||||||
const { dir, name } = parse(mainJsFile);
|
const cjsExports = getExports({
|
||||||
const cjsMain = `${dir ? dir : '.'}/${name}${
|
...options,
|
||||||
options.outputFileExtensionForCjs ?? '.js'
|
fileExt: options.outputFileExtensionForCjs ?? '.js',
|
||||||
}`;
|
});
|
||||||
packageJson.main ??= cjsMain;
|
|
||||||
if (typeof exports !== 'string') {
|
packageJson.main = cjsExports['.'];
|
||||||
if (typeof exports['.'] !== 'string') {
|
if (!hasEsmFormat) {
|
||||||
exports['.']['require'] ??= cjsMain;
|
packageJson.type = 'commonjs';
|
||||||
} else if (!hasEsmFormat) {
|
}
|
||||||
exports['.'] ??= cjsMain;
|
|
||||||
|
if (options.generateExportsField) {
|
||||||
|
for (const [exportEntry, filePath] of Object.entries(cjsExports)) {
|
||||||
|
if (hasEsmFormat) {
|
||||||
|
packageJson.exports[exportEntry]['default'] ??= filePath;
|
||||||
|
} else {
|
||||||
|
packageJson.exports[exportEntry] = filePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.generateExportsField) {
|
|
||||||
packageJson.exports = exports;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.skipTypings) {
|
if (!options.skipTypings) {
|
||||||
|
const mainFile = basename(options.main).replace(/\.[tj]s$/, '');
|
||||||
|
const relativeMainFileDir = getRelativeDirectoryToProjectRoot(
|
||||||
|
options.main,
|
||||||
|
options.projectRoot
|
||||||
|
);
|
||||||
|
const typingsFile = `${relativeMainFileDir}${mainFile}.d.ts`;
|
||||||
packageJson.types = packageJson.types ?? typingsFile;
|
packageJson.types = packageJson.types ?? typingsFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
packages/js/src/utils/schema.d.ts
vendored
2
packages/js/src/utils/schema.d.ts
vendored
@ -38,6 +38,8 @@ export interface ExecutorOptions {
|
|||||||
rootDir?: string;
|
rootDir?: string;
|
||||||
outputPath: string;
|
outputPath: string;
|
||||||
tsConfig: string;
|
tsConfig: string;
|
||||||
|
generateExportsField?: boolean;
|
||||||
|
additionalEntryPoints?: string[];
|
||||||
swcrc?: string;
|
swcrc?: string;
|
||||||
watch: boolean;
|
watch: boolean;
|
||||||
clean?: boolean;
|
clean?: boolean;
|
||||||
|
|||||||
@ -20,7 +20,9 @@ function getSwcCmd(
|
|||||||
// TODO(jack): clean this up when we remove inline module support
|
// TODO(jack): clean this up when we remove inline module support
|
||||||
// Handle root project
|
// Handle root project
|
||||||
srcPath === '.' ? 'src' : srcPath
|
srcPath === '.' ? 'src' : srcPath
|
||||||
} -d ${destPath} --config-file=${swcrcPath}`;
|
} -d ${
|
||||||
|
srcPath === '.' ? `${destPath}/src` : destPath
|
||||||
|
} --config-file=${swcrcPath}`;
|
||||||
return watch ? swcCmd.concat(' --watch') : swcCmd;
|
return watch ? swcCmd.concat(' --watch') : swcCmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { ensureTypescript } from './ensure-typescript';
|
|||||||
|
|
||||||
let tsModule: typeof import('typescript');
|
let tsModule: typeof import('typescript');
|
||||||
|
|
||||||
export function readTsConfig(tsConfigPath: string) {
|
export function readTsConfig(tsConfigPath: string): ts.ParsedCommandLine {
|
||||||
if (!tsModule) {
|
if (!tsModule) {
|
||||||
tsModule = require('typescript');
|
tsModule = require('typescript');
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user