Skip to content

Commit

Permalink
feat(module-tools): support autoExtension and shims to improve node e…
Browse files Browse the repository at this point in the history
…sm mode (#4831)
  • Loading branch information
10Derozan authored Oct 25, 2023
1 parent 472f065 commit 44fea2c
Show file tree
Hide file tree
Showing 38 changed files with 400 additions and 19 deletions.
6 changes: 6 additions & 0 deletions .changeset/orange-garlics-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@modern-js/module-tools': minor
---

feat(module-tools): support autoExtension and shims to improve node esm mode
feat(module-tools): 支持 autoExtension 和 shims 配置以完善 node esm 模式
2 changes: 0 additions & 2 deletions packages/review/eslint-config-app/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -1080,8 +1080,6 @@ module.exports = {
'import/no-default-export': 'off',
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-named-export.md
'import/no-named-export': 'off',
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-useless-path-segments.md
'import/no-useless-path-segments': ['error', { noUselessIndex: true }],
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-cycle.md
'import/no-cycle': 'off',
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/dynamic-import-chunkname.md
Expand Down
17 changes: 17 additions & 0 deletions packages/solutions/module-tools/shims/cjs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* eslint-disable node/prefer-global/url */
/* eslint-disable no-undef */
// taking from https://github.com/egoist/tsup/blob/dev/assets/cjs_shims.js

// Shim globals in cjs bundle
// There's a weird bug that esbuild will always inject importMetaUrl
// if we export it as `const importMetaUrl = ... __filename ...`
// But using a function will not cause this issue

const getImportMetaUrl = () =>
typeof document === 'undefined'
? new URL(`file:${__filename}`).href
: (document.currentScript && document.currentScript.src) ||
new URL('main.js', document.baseURI).href;

// define importMetaUrl
export const importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
9 changes: 9 additions & 0 deletions packages/solutions/module-tools/shims/esm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Shim globals in esm bundle
import { fileURLToPath } from 'url';
import path from 'path';

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

export const __dirname = /* @__PURE__ */ getDirname();
export const __filename = /* @__PURE__ */ getFilename();
10 changes: 9 additions & 1 deletion packages/solutions/module-tools/src/builder/build.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { slash, logger, fs } from '@modern-js/utils';
import { withLogTitle } from '../utils';
import { withLogTitle, getDefaultOutExtension } from '../utils';
import type {
BuildCommandOptions,
BaseBuildConfig,
Expand Down Expand Up @@ -63,13 +63,20 @@ export const generatorDts = async (
tsconfig,
footer: { dts: footer },
banner: { dts: banner },
format,
autoExtension,
} = config;
const { appDirectory } = api.useAppContext();
const { distPath, abortOnError, respectExternal } = dts;

// remove this line after remove dts.tsconfigPath
const tsconfigPath = dts.tsconfigPath ?? tsconfig;

const { dtsExtension } = getDefaultOutExtension({
format,
root: appDirectory,
autoExtension,
});
const generatorDtsConfig = {
distPath,
watch,
Expand All @@ -83,6 +90,7 @@ export const generatorDts = async (
banner,
alias,
sourceDir,
dtsExtension,
};
const prevTime = Date.now();
debug(`${label('dts')} Build Start`);
Expand Down
2 changes: 2 additions & 0 deletions packages/solutions/module-tools/src/builder/dts/rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const runRollup = async (
appDirectory,
footer,
banner,
dtsExtension,
}: GeneratorDtsConfig,
) => {
const ignoreFiles: Plugin = {
Expand Down Expand Up @@ -110,6 +111,7 @@ export const runRollup = async (
exports: 'named',
footer,
banner,
entryFileNames: `[name]${dtsExtension}`,
};
if (watch) {
const { watch } = await import('../../../compiled/rollup');
Expand Down
35 changes: 31 additions & 4 deletions packages/solutions/module-tools/src/builder/esbuild/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import path from 'path';
import {
BuildResult,
BuildOptions,
Expand All @@ -11,7 +12,7 @@ import {
} from 'esbuild';
import * as tapable from 'tapable';
import { FSWatcher, chalk, logger, fs, lodash } from '@modern-js/utils';
import { withLogTitle } from '../../utils';
import { withLogTitle, getDefaultOutExtension } from '../../utils';
import {
BaseBuildConfig,
BuilderHooks,
Expand Down Expand Up @@ -138,24 +139,50 @@ export class EsbuildCompiler implements ICompiler {
tsconfig,
banner,
footer,
shims,
autoExtension,
} = config;

const bundle = buildType === 'bundle';
const entryNames = bundle ? '[name]' : '[dir]/[name]';
const absWorkingDir = this.context.root;
let esbuildFormat: Format = format === 'umd' ? 'esm' : format;
if (bundle && format === 'cjs' && splitting) {
esbuildFormat = 'esm';
}
const esbuildTarget = target === 'es5' ? undefined : target;
const jsExtensions = ['.jsx', '.tsx', '.js', '.ts', '.json'];
const { jsExtension } = getDefaultOutExtension({
format,
root: this.context.root,
autoExtension,
});

const buildOptions: BuildOptions = {
inject: [
format === 'cjs' && shims
? path.join(__dirname, '../../../shims/cjs.js')
: '',
format === 'esm' && shims && platform === 'node'
? path.join(__dirname, '../../../shims/esm.js')
: '',
].filter(Boolean),
outExtension: autoExtension
? {
'.js': jsExtension,
}
: undefined,
banner: lodash.pick(banner, ['js', 'css']),
footer: lodash.pick(footer, ['js', 'css']),
entryPoints: input,
metafile: true,
define,
define: {
...(format === 'cjs' && shims
? {
'import.meta.url': 'importMetaUrl',
}
: {}),
...define,
},
bundle: buildType === 'bundle',
format: esbuildFormat,
target: esbuildTarget,
Expand All @@ -164,7 +191,7 @@ export class EsbuildCompiler implements ICompiler {
splitting,
charset: 'utf8',
logLimit: 5,
absWorkingDir,
absWorkingDir: this.context.root,
platform,
tsconfig: fs.existsSync(tsconfig) ? tsconfig : undefined,
write: false,
Expand Down
29 changes: 27 additions & 2 deletions packages/solutions/module-tools/src/builder/feature/redirect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
isJsExt,
isJsLoader,
resolvePathAndQuery,
getDefaultOutExtension,
isTsExt,
} from '../../utils';
import { getAssetContents, loadSvgr } from './asset';
import { isCssModule } from './style/postcssTransformer';
Expand All @@ -43,6 +45,7 @@ async function redirectImport(
aliasRecord: Record<string, string>,
filePath: string,
outputDir: string,
jsExtension: string,
matchPath?: MatchPath,
): Promise<MagicString> {
const str: MagicString = new MagicString(code);
Expand All @@ -54,6 +57,7 @@ async function redirectImport(
}
const { start, end } = module;
let { name } = module;
const ext = extname(name);

const { redirect, asset } = compiler.config;
const { alias, style } = redirect;
Expand Down Expand Up @@ -89,9 +93,22 @@ async function redirectImport(
}
}

if (redirect.autoExtension) {
if (ext === '' && jsExtension !== '.js' && name.startsWith('.')) {
// add extension for relative path, no check if it's a directory.
str.overwrite(start, end, `${name}${jsExtension}`);
return;
}

if (isTsExt(name)) {
// .c(m)ts -> jsExtension
str.overwrite(start, end, name.replace(/\.(m|c)?tsx?$/, jsExtension));
return;
}
}

if (style) {
// redirect style path
const ext = extname(name);
const { originalFilePath, query } = resolvePathAndQuery(name);

if (query.css_virtual) {
Expand Down Expand Up @@ -181,7 +198,9 @@ export const redirect = {
return args;
}
const { code, path: id } = args;
const { format, alias, sourceDir, outDir } = compiler.config;
const { format, alias, sourceDir, outDir, autoExtension } =
compiler.config;
const { root } = compiler.context;

if (!code || format === 'iife' || format === 'umd') {
return args;
Expand Down Expand Up @@ -249,6 +268,11 @@ export const redirect = {
if (!matchModule.length) {
return args;
}
const { jsExtension } = getDefaultOutExtension({
format,
root,
autoExtension,
});
const outputPath = resolve(outDir, relative(sourceDir, id));
const str = await redirectImport(
compiler,
Expand All @@ -257,6 +281,7 @@ export const redirect = {
absoluteAlias,
id,
dirname(outputPath),
jsExtension,
matchPath,
);
return {
Expand Down
2 changes: 2 additions & 0 deletions packages/solutions/module-tools/src/config/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ export const mergeDefaultBaseConfig = async (

const esbuildOptions = pConfig.esbuildOptions ?? defaultConfig.esbuildOptions;
return {
shims: pConfig.shims ?? defaultConfig.shims,
autoExtension: pConfig.autoExtension ?? defaultConfig.autoExtension,
footer: pConfig.footer ?? defaultConfig.footer,
banner: pConfig.banner ?? defaultConfig.banner,
resolve,
Expand Down
6 changes: 6 additions & 0 deletions packages/solutions/module-tools/src/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ const buildConfigProperties = {
},
else: { type: 'boolean' },
},
autoExtension: {
type: 'boolean',
},
externals: {
type: 'array',
items: {
Expand Down Expand Up @@ -172,6 +175,9 @@ const buildConfigProperties = {
},
],
},
shims: {
type: 'boolean',
},
sideEffects: {
anyOf: [
{
Expand Down
3 changes: 3 additions & 0 deletions packages/solutions/module-tools/src/constants/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const getDefaultBuildConfig = () => {
abortOnError: true,
respectExternal: true,
}),
autoExtension: false,
esbuildOptions: c => c,
externalHelpers: false,
externals: [],
Expand All @@ -38,11 +39,13 @@ export const getDefaultBuildConfig = () => {
alias: true,
asset: true,
style: true,
autoExtension: true,
},
resolve: {
mainFields: ['module', 'main'],
jsExtensions: ['.jsx', '.tsx', '.js', '.ts', '.json'],
},
shims: false,
sideEffects: undefined,
sourceDir: './src',
sourceMap: false,
Expand Down
3 changes: 3 additions & 0 deletions packages/solutions/module-tools/src/types/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export type Redirect = {
alias?: boolean;
style?: boolean;
asset?: boolean;
autoExtension?: boolean;
};

export type DTSOptions = {
Expand Down Expand Up @@ -144,6 +145,8 @@ export type BaseBuildConfig = Omit<
};

export type PartialBaseBuildConfig = {
shims?: boolean;
autoExtension?: boolean;
resolve?: Resolve;
footer?: BannerAndFooter;
banner?: BannerAndFooter;
Expand Down
1 change: 1 addition & 0 deletions packages/solutions/module-tools/src/types/dts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface GeneratorDtsConfig {
externals: BaseBuildConfig['externals'];
input: Input;
respectExternal: boolean;
dtsExtension: string;
}

export interface GeneratedDtsInfo {
Expand Down
26 changes: 17 additions & 9 deletions packages/solutions/module-tools/src/utils/dts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,24 @@ export const writeDtsFiles = async (
},
result: { path: string; content: string }[],
) => {
const { distPath } = config;
const { distPath, dtsExtension } = config;
const { tempDistAbsSrcPath } = options;

for (const r of result) {
fs.writeFileSync(r.path, r.content);
}

// why use `ensureDir` before copy? look this: https://github.com/jprichardson/node-fs-extra/issues/957
await fs.ensureDir(distPath);
await fs.copy(tempDistAbsSrcPath, distPath);
// write to dist
await Promise.all(
result.map(({ path, content }) => {
const relativePath = relative(tempDistAbsSrcPath, path);
const filepath = join(
distPath,
relativePath.replace(/\.d\.ts/, dtsExtension),
);
fs.ensureFileSync(filepath);
return fs.writeFile(
// only replace .d.ts, if tsc generate .d.m(c)ts, keep.
filepath,
content,
);
}),
);
};

export const addBannerAndFooter = (
Expand Down
1 change: 1 addition & 0 deletions packages/solutions/module-tools/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './map';
export * from './print';
export * from './style';
export * from './tspath';
export * from './outExtension';
Loading

0 comments on commit 44fea2c

Please sign in to comment.