diff --git a/.changeset/orange-garlics-beg.md b/.changeset/orange-garlics-beg.md new file mode 100644 index 000000000000..23ddf3a65012 --- /dev/null +++ b/.changeset/orange-garlics-beg.md @@ -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 模式 diff --git a/packages/review/eslint-config-app/base.js b/packages/review/eslint-config-app/base.js index 94c65c56e653..ed93e71ce7da 100644 --- a/packages/review/eslint-config-app/base.js +++ b/packages/review/eslint-config-app/base.js @@ -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 diff --git a/packages/solutions/module-tools/shims/cjs.js b/packages/solutions/module-tools/shims/cjs.js new file mode 100644 index 000000000000..a349c7abef95 --- /dev/null +++ b/packages/solutions/module-tools/shims/cjs.js @@ -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(); diff --git a/packages/solutions/module-tools/shims/esm.js b/packages/solutions/module-tools/shims/esm.js new file mode 100644 index 000000000000..13b8726e18c0 --- /dev/null +++ b/packages/solutions/module-tools/shims/esm.js @@ -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(); diff --git a/packages/solutions/module-tools/src/builder/build.ts b/packages/solutions/module-tools/src/builder/build.ts index 97da92f43f29..063e3ba37240 100644 --- a/packages/solutions/module-tools/src/builder/build.ts +++ b/packages/solutions/module-tools/src/builder/build.ts @@ -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, @@ -63,6 +63,8 @@ export const generatorDts = async ( tsconfig, footer: { dts: footer }, banner: { dts: banner }, + format, + autoExtension, } = config; const { appDirectory } = api.useAppContext(); const { distPath, abortOnError, respectExternal } = dts; @@ -70,6 +72,11 @@ export const generatorDts = async ( // remove this line after remove dts.tsconfigPath const tsconfigPath = dts.tsconfigPath ?? tsconfig; + const { dtsExtension } = getDefaultOutExtension({ + format, + root: appDirectory, + autoExtension, + }); const generatorDtsConfig = { distPath, watch, @@ -83,6 +90,7 @@ export const generatorDts = async ( banner, alias, sourceDir, + dtsExtension, }; const prevTime = Date.now(); debug(`${label('dts')} Build Start`); diff --git a/packages/solutions/module-tools/src/builder/dts/rollup.ts b/packages/solutions/module-tools/src/builder/dts/rollup.ts index 6ac5c31e1549..f84c57ddd4d2 100644 --- a/packages/solutions/module-tools/src/builder/dts/rollup.ts +++ b/packages/solutions/module-tools/src/builder/dts/rollup.ts @@ -32,6 +32,7 @@ export const runRollup = async ( appDirectory, footer, banner, + dtsExtension, }: GeneratorDtsConfig, ) => { const ignoreFiles: Plugin = { @@ -110,6 +111,7 @@ export const runRollup = async ( exports: 'named', footer, banner, + entryFileNames: `[name]${dtsExtension}`, }; if (watch) { const { watch } = await import('../../../compiled/rollup'); diff --git a/packages/solutions/module-tools/src/builder/esbuild/index.ts b/packages/solutions/module-tools/src/builder/esbuild/index.ts index fc696b9f9dbd..4383bee66e58 100644 --- a/packages/solutions/module-tools/src/builder/esbuild/index.ts +++ b/packages/solutions/module-tools/src/builder/esbuild/index.ts @@ -1,3 +1,4 @@ +import path from 'path'; import { BuildResult, BuildOptions, @@ -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, @@ -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, @@ -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, diff --git a/packages/solutions/module-tools/src/builder/feature/redirect.ts b/packages/solutions/module-tools/src/builder/feature/redirect.ts index fdff852f0f9e..829d0c911b49 100644 --- a/packages/solutions/module-tools/src/builder/feature/redirect.ts +++ b/packages/solutions/module-tools/src/builder/feature/redirect.ts @@ -26,6 +26,8 @@ import { isJsExt, isJsLoader, resolvePathAndQuery, + getDefaultOutExtension, + isTsExt, } from '../../utils'; import { getAssetContents, loadSvgr } from './asset'; import { isCssModule } from './style/postcssTransformer'; @@ -43,6 +45,7 @@ async function redirectImport( aliasRecord: Record, filePath: string, outputDir: string, + jsExtension: string, matchPath?: MatchPath, ): Promise { const str: MagicString = new MagicString(code); @@ -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; @@ -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) { @@ -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; @@ -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, @@ -257,6 +281,7 @@ export const redirect = { absoluteAlias, id, dirname(outputPath), + jsExtension, matchPath, ); return { diff --git a/packages/solutions/module-tools/src/config/merge.ts b/packages/solutions/module-tools/src/config/merge.ts index 61405b5c1cec..b498c03320af 100644 --- a/packages/solutions/module-tools/src/config/merge.ts +++ b/packages/solutions/module-tools/src/config/merge.ts @@ -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, diff --git a/packages/solutions/module-tools/src/config/schema.ts b/packages/solutions/module-tools/src/config/schema.ts index fe230c1432d8..d19c00b66cfd 100644 --- a/packages/solutions/module-tools/src/config/schema.ts +++ b/packages/solutions/module-tools/src/config/schema.ts @@ -105,6 +105,9 @@ const buildConfigProperties = { }, else: { type: 'boolean' }, }, + autoExtension: { + type: 'boolean', + }, externals: { type: 'array', items: { @@ -172,6 +175,9 @@ const buildConfigProperties = { }, ], }, + shims: { + type: 'boolean', + }, sideEffects: { anyOf: [ { diff --git a/packages/solutions/module-tools/src/constants/build.ts b/packages/solutions/module-tools/src/constants/build.ts index 255b728c6784..a3e7a103dc94 100644 --- a/packages/solutions/module-tools/src/constants/build.ts +++ b/packages/solutions/module-tools/src/constants/build.ts @@ -22,6 +22,7 @@ export const getDefaultBuildConfig = () => { abortOnError: true, respectExternal: true, }), + autoExtension: false, esbuildOptions: c => c, externalHelpers: false, externals: [], @@ -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, diff --git a/packages/solutions/module-tools/src/types/config/index.ts b/packages/solutions/module-tools/src/types/config/index.ts index 12d967775104..efcc0cc78d99 100644 --- a/packages/solutions/module-tools/src/types/config/index.ts +++ b/packages/solutions/module-tools/src/types/config/index.ts @@ -69,6 +69,7 @@ export type Redirect = { alias?: boolean; style?: boolean; asset?: boolean; + autoExtension?: boolean; }; export type DTSOptions = { @@ -144,6 +145,8 @@ export type BaseBuildConfig = Omit< }; export type PartialBaseBuildConfig = { + shims?: boolean; + autoExtension?: boolean; resolve?: Resolve; footer?: BannerAndFooter; banner?: BannerAndFooter; diff --git a/packages/solutions/module-tools/src/types/dts.ts b/packages/solutions/module-tools/src/types/dts.ts index 34a6d1000df2..d786c2d9afae 100644 --- a/packages/solutions/module-tools/src/types/dts.ts +++ b/packages/solutions/module-tools/src/types/dts.ts @@ -13,6 +13,7 @@ export interface GeneratorDtsConfig { externals: BaseBuildConfig['externals']; input: Input; respectExternal: boolean; + dtsExtension: string; } export interface GeneratedDtsInfo { diff --git a/packages/solutions/module-tools/src/utils/dts.ts b/packages/solutions/module-tools/src/utils/dts.ts index 65d565932e75..da2efa93d0e7 100644 --- a/packages/solutions/module-tools/src/utils/dts.ts +++ b/packages/solutions/module-tools/src/utils/dts.ts @@ -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 = ( diff --git a/packages/solutions/module-tools/src/utils/index.ts b/packages/solutions/module-tools/src/utils/index.ts index cebd81124503..5309b67a49ff 100644 --- a/packages/solutions/module-tools/src/utils/index.ts +++ b/packages/solutions/module-tools/src/utils/index.ts @@ -9,3 +9,4 @@ export * from './map'; export * from './print'; export * from './style'; export * from './tspath'; +export * from './outExtension'; diff --git a/packages/solutions/module-tools/src/utils/outExtension.ts b/packages/solutions/module-tools/src/utils/outExtension.ts new file mode 100644 index 000000000000..47bb40cc5250 --- /dev/null +++ b/packages/solutions/module-tools/src/utils/outExtension.ts @@ -0,0 +1,45 @@ +import path from 'path'; +import { fs, logger } from '@modern-js/utils'; +import type { Format } from '../types'; + +export const getDefaultOutExtension = (options: { + format: Format; + root: string; + autoExtension: boolean; +}) => { + const { format, root, autoExtension } = options; + let jsExtension = '.js'; + let dtsExtension = '.d.ts'; + if (!autoExtension) { + return { + jsExtension, + dtsExtension, + }; + } + let isModule = false; + try { + const json = JSON.parse( + fs.readFileSync(path.resolve(root, './package.json'), 'utf8'), + ); + isModule = json.type === 'module'; + } catch (e) { + logger.warn('package.json is broken'); + return { + jsExtension, + dtsExtension, + }; + } + + if (isModule && format === 'cjs') { + jsExtension = '.cjs'; + dtsExtension = '.d.cts'; + } + if (!isModule && format === 'esm') { + jsExtension = '.mjs'; + dtsExtension = '.d.mts'; + } + return { + jsExtension, + dtsExtension, + }; +}; diff --git a/tests/integration/module/fixtures/build/autoExtension/type-commonjs/modern.config.ts b/tests/integration/module/fixtures/build/autoExtension/type-commonjs/modern.config.ts new file mode 100644 index 000000000000..dc3caa7e25b7 --- /dev/null +++ b/tests/integration/module/fixtures/build/autoExtension/type-commonjs/modern.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from '@modern-js/module-tools/defineConfig'; + +export default defineConfig({ + buildConfig: [ + { + buildType: 'bundle', + autoExtension: true, + input: { bundle: './src/index.ts' }, + format: 'esm', + }, + { + buildType: 'bundleless', + autoExtension: true, + format: 'esm', + }, + ], +}); diff --git a/tests/integration/module/fixtures/build/autoExtension/type-commonjs/package.json b/tests/integration/module/fixtures/build/autoExtension/type-commonjs/package.json new file mode 100644 index 000000000000..ca6463589e8b --- /dev/null +++ b/tests/integration/module/fixtures/build/autoExtension/type-commonjs/package.json @@ -0,0 +1,4 @@ +{ + "name": "auto-extension-commonjs-test", + "version": "1.0.0" +} diff --git a/tests/integration/module/fixtures/build/autoExtension/type-commonjs/src/common.ts b/tests/integration/module/fixtures/build/autoExtension/type-commonjs/src/common.ts new file mode 100644 index 000000000000..20eb7f1406cc --- /dev/null +++ b/tests/integration/module/fixtures/build/autoExtension/type-commonjs/src/common.ts @@ -0,0 +1 @@ +export const addPrefix = (prefix: string, str: string) => `${prefix}:${str}`; diff --git a/tests/integration/module/fixtures/build/autoExtension/type-commonjs/src/index.ts b/tests/integration/module/fixtures/build/autoExtension/type-commonjs/src/index.ts new file mode 100644 index 000000000000..323d353d5fae --- /dev/null +++ b/tests/integration/module/fixtures/build/autoExtension/type-commonjs/src/index.ts @@ -0,0 +1,6 @@ +import path from 'path'; + +export const debug = async (str: string) => { + const { addPrefix } = await import('./common'); + addPrefix('DEBUG:', path.join(str)); +}; diff --git a/tests/integration/module/fixtures/build/autoExtension/type-commonjs/tsconfig.json b/tests/integration/module/fixtures/build/autoExtension/type-commonjs/tsconfig.json new file mode 100644 index 000000000000..a7949e4c0bf7 --- /dev/null +++ b/tests/integration/module/fixtures/build/autoExtension/type-commonjs/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@modern-js/tsconfig/base", + "compilerOptions": { + "baseUrl": "./", + "paths": { + "@src/*": ["./src/*"] + } + }, + "include": ["src"] +} diff --git a/tests/integration/module/fixtures/build/autoExtension/type-commonjs/type-commonjs.test.ts b/tests/integration/module/fixtures/build/autoExtension/type-commonjs/type-commonjs.test.ts new file mode 100644 index 000000000000..199f8a0bdc78 --- /dev/null +++ b/tests/integration/module/fixtures/build/autoExtension/type-commonjs/type-commonjs.test.ts @@ -0,0 +1,28 @@ +import path from 'path'; +import { globby, fs } from '@modern-js/utils'; +import { runCli, initBeforeTest } from '../../../utils'; + +initBeforeTest(); + +describe('autoExtension', () => { + const fixtureDir = __dirname; + it('type:module', async () => { + await runCli({ + argv: ['build'], + appDirectory: fixtureDir, + enableDts: true, + }); + const cwd = path.join(fixtureDir, 'dist'); + const outputDeclarationFile = await globby('*.d.mts', { + cwd, + }); + const outputCjsFile = await globby('*.d.mts', { + cwd, + }); + expect( + outputDeclarationFile.length === 3 && outputCjsFile.length === 3, + ).toBeTruthy(); + const content = await fs.readFile(path.join(cwd, 'index.mjs'), 'utf-8'); + expect(content.includes('./common.mjs')); + }); +}); diff --git a/tests/integration/module/fixtures/build/autoExtension/type-module/modern.config.ts b/tests/integration/module/fixtures/build/autoExtension/type-module/modern.config.ts new file mode 100644 index 000000000000..7ee7dd1b4a7b --- /dev/null +++ b/tests/integration/module/fixtures/build/autoExtension/type-module/modern.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from '@modern-js/module-tools/defineConfig'; + +export default defineConfig({ + buildConfig: [ + { + buildType: 'bundle', + input: { bundle: './src/index.ts' }, + autoExtension: true, + format: 'cjs', + }, + + { + buildType: 'bundleless', + autoExtension: true, + format: 'cjs', + }, + ], +}); diff --git a/tests/integration/module/fixtures/build/autoExtension/type-module/package.json b/tests/integration/module/fixtures/build/autoExtension/type-module/package.json new file mode 100644 index 000000000000..5a6b9862eae2 --- /dev/null +++ b/tests/integration/module/fixtures/build/autoExtension/type-module/package.json @@ -0,0 +1,5 @@ +{ + "name": "auto-extension-type-module-test", + "version": "1.0.0", + "type": "module" +} diff --git a/tests/integration/module/fixtures/build/autoExtension/type-module/src/common.ts b/tests/integration/module/fixtures/build/autoExtension/type-module/src/common.ts new file mode 100644 index 000000000000..20eb7f1406cc --- /dev/null +++ b/tests/integration/module/fixtures/build/autoExtension/type-module/src/common.ts @@ -0,0 +1 @@ +export const addPrefix = (prefix: string, str: string) => `${prefix}:${str}`; diff --git a/tests/integration/module/fixtures/build/autoExtension/type-module/src/index.ts b/tests/integration/module/fixtures/build/autoExtension/type-module/src/index.ts new file mode 100644 index 000000000000..323d353d5fae --- /dev/null +++ b/tests/integration/module/fixtures/build/autoExtension/type-module/src/index.ts @@ -0,0 +1,6 @@ +import path from 'path'; + +export const debug = async (str: string) => { + const { addPrefix } = await import('./common'); + addPrefix('DEBUG:', path.join(str)); +}; diff --git a/tests/integration/module/fixtures/build/autoExtension/type-module/tsconfig.json b/tests/integration/module/fixtures/build/autoExtension/type-module/tsconfig.json new file mode 100644 index 000000000000..a7949e4c0bf7 --- /dev/null +++ b/tests/integration/module/fixtures/build/autoExtension/type-module/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@modern-js/tsconfig/base", + "compilerOptions": { + "baseUrl": "./", + "paths": { + "@src/*": ["./src/*"] + } + }, + "include": ["src"] +} diff --git a/tests/integration/module/fixtures/build/autoExtension/type-module/type-module.test.ts b/tests/integration/module/fixtures/build/autoExtension/type-module/type-module.test.ts new file mode 100644 index 000000000000..4c3d28da685e --- /dev/null +++ b/tests/integration/module/fixtures/build/autoExtension/type-module/type-module.test.ts @@ -0,0 +1,28 @@ +import path from 'path'; +import { globby, fs } from '@modern-js/utils'; +import { runCli, initBeforeTest } from '../../../utils'; + +initBeforeTest(); + +describe('autoExtension', () => { + const fixtureDir = __dirname; + it('type:module', async () => { + await runCli({ + argv: ['build'], + appDirectory: fixtureDir, + enableDts: true, + }); + const cwd = path.join(fixtureDir, 'dist'); + const outputDeclarationFile = await globby('*.d.cts', { + cwd, + }); + const outputCjsFile = await globby('*.d.cts', { + cwd, + }); + expect( + outputDeclarationFile.length === 3 && outputCjsFile.length === 3, + ).toBeTruthy(); + const content = await fs.readFile(path.join(cwd, 'index.cjs'), 'utf-8'); + expect(content.includes('./common.cjs')); + }); +}); diff --git a/tests/integration/module/fixtures/build/redirect/package.json b/tests/integration/module/fixtures/build/redirect/package.json index ecd77f1dd286..db0421422ea8 100644 --- a/tests/integration/module/fixtures/build/redirect/package.json +++ b/tests/integration/module/fixtures/build/redirect/package.json @@ -1,4 +1,5 @@ { "name": "redirect-test", - "version": "1.0.0" + "version": "1.0.0", + "type": "module" } diff --git a/tests/integration/module/fixtures/build/redirect/redirect.test.ts b/tests/integration/module/fixtures/build/redirect/redirect.test.ts index 90dfe5cb1cde..8e1898e283dd 100644 --- a/tests/integration/module/fixtures/build/redirect/redirect.test.ts +++ b/tests/integration/module/fixtures/build/redirect/redirect.test.ts @@ -31,6 +31,8 @@ describe('redirect', () => { const jsContent = await fs.readFile(distJsFilePath, 'utf-8'); // redirect alias expect(jsContent.includes(`@/alias`)).toBeFalsy(); + // redirect autoExtension + expect(jsContent.includes(`./extension.js`)).toBeTruthy(); // redirect style expect( jsContent.includes(`import css from "./index.module";`), diff --git a/tests/integration/module/fixtures/build/redirect/redirect.ts b/tests/integration/module/fixtures/build/redirect/redirect.ts index 0ec2f45058ab..720e94075c71 100644 --- a/tests/integration/module/fixtures/build/redirect/redirect.ts +++ b/tests/integration/module/fixtures/build/redirect/redirect.ts @@ -7,5 +7,6 @@ export default defineConfig({ target: 'esnext', outDir: './dist/redirect', dts: false, + autoExtension: true, }, }); diff --git a/tests/integration/module/fixtures/build/redirect/src/extension.mts b/tests/integration/module/fixtures/build/redirect/src/extension.mts new file mode 100644 index 000000000000..90b507374599 --- /dev/null +++ b/tests/integration/module/fixtures/build/redirect/src/extension.mts @@ -0,0 +1 @@ +export const extension = '.mts'; diff --git a/tests/integration/module/fixtures/build/redirect/src/index.ts b/tests/integration/module/fixtures/build/redirect/src/index.ts index 749999588095..e3d6de0c0e25 100644 --- a/tests/integration/module/fixtures/build/redirect/src/index.ts +++ b/tests/integration/module/fixtures/build/redirect/src/index.ts @@ -12,4 +12,7 @@ export { a, b } from '@/alias'; export * from '@/alias'; export * as c from '@/alias'; +// autoExtension +export * from './extension.mts'; + console.log(css, svg, a, b, namedImport, wildcardImport); diff --git a/tests/integration/module/fixtures/build/shims/modern.config.ts b/tests/integration/module/fixtures/build/shims/modern.config.ts new file mode 100644 index 000000000000..5c0ab5dfbee7 --- /dev/null +++ b/tests/integration/module/fixtures/build/shims/modern.config.ts @@ -0,0 +1,36 @@ +import { defineConfig } from '@modern-js/module-tools/defineConfig'; + +export default defineConfig({ + buildConfig: [ + { + buildType: 'bundle', + shims: true, + input: { bundle: './src/index.ts' }, + format: 'esm', + outDir: 'dist/esm', + target: 'esnext', + autoExtension: true, + }, + { + buildType: 'bundle', + input: { bundle: './src/index.ts' }, + shims: true, + format: 'cjs', + outDir: 'dist/cjs', + }, + { + buildType: 'bundleless', + shims: true, + format: 'esm', + outDir: 'dist/esm', + target: 'esnext', + autoExtension: true, + }, + { + buildType: 'bundleless', + shims: true, + format: 'cjs', + outDir: 'dist/cjs', + }, + ], +}); diff --git a/tests/integration/module/fixtures/build/shims/package.json b/tests/integration/module/fixtures/build/shims/package.json new file mode 100644 index 000000000000..65d51b9a38db --- /dev/null +++ b/tests/integration/module/fixtures/build/shims/package.json @@ -0,0 +1,4 @@ +{ + "name": "shims-test", + "version": "1.0.0" +} diff --git a/tests/integration/module/fixtures/build/shims/shims.test.ts b/tests/integration/module/fixtures/build/shims/shims.test.ts new file mode 100644 index 000000000000..4d6a3e35210e --- /dev/null +++ b/tests/integration/module/fixtures/build/shims/shims.test.ts @@ -0,0 +1,29 @@ +import path from 'path'; +import { execa } from '@modern-js/utils'; +import { runCli, initBeforeTest } from '../../utils'; + +initBeforeTest(); + +describe('shims', () => { + const fixtureDir = __dirname; + it('shims', async () => { + const { success } = await runCli({ + argv: ['build'], + appDirectory: fixtureDir, + }); + expect(success).toBeTruthy(); + const cjs_bundle_path = path.join(fixtureDir, 'dist/cjs/bundle.js'); + const cjs_bundleless_path = path.join(fixtureDir, 'dist/cjs/index.js'); + const esm_bundle_path = path.join(fixtureDir, 'dist/esm/bundle.mjs'); + const esm_bundleless_path = path.join(fixtureDir, 'dist/esm/index.mjs'); + + const { command } = execa; + const output = await Promise.all([ + command(`node ${cjs_bundle_path}`), + command(`node ${cjs_bundleless_path}`), + command(`node ${esm_bundle_path}`), + command(`node ${esm_bundleless_path}`), + ]); + expect(output.every(o => !o.failed)).toBeTruthy(); + }); +}); diff --git a/tests/integration/module/fixtures/build/shims/src/common.ts b/tests/integration/module/fixtures/build/shims/src/common.ts new file mode 100644 index 000000000000..20eb7f1406cc --- /dev/null +++ b/tests/integration/module/fixtures/build/shims/src/common.ts @@ -0,0 +1 @@ +export const addPrefix = (prefix: string, str: string) => `${prefix}:${str}`; diff --git a/tests/integration/module/fixtures/build/shims/src/index.ts b/tests/integration/module/fixtures/build/shims/src/index.ts new file mode 100644 index 000000000000..dfcfb96e4e97 --- /dev/null +++ b/tests/integration/module/fixtures/build/shims/src/index.ts @@ -0,0 +1,8 @@ +import path from 'path'; + +export const debug = async (str: string) => { + const { addPrefix } = await import('./common'); + addPrefix('DEBUG:', path.join(str)); +}; + +console.log(__dirname, import.meta.url);