From e7bcaf10b874659f6b8d81fb40f7f954dd2d4012 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Mon, 22 Jul 2024 11:59:48 +0800 Subject: [PATCH] feat: implement basic bundleless support (#40) --- .vscode/settings.json | 3 +- CONTRIBUTING.md | 6 +- .../alias/__snapshots__/index.test.ts.snap | 6 +- e2e/cases/alias/index.test.ts | 12 +- e2e/cases/autoExtension/index.test.ts | 14 +- e2e/cases/bundle-false/basic/rslib.config.ts | 18 +++ e2e/cases/bundle-false/basic/src/index.ts | 3 + e2e/cases/bundle-false/basic/src/sum.ts | 5 + .../bundle-false/basic/src/utils/numbers.ts | 3 + .../bundle-false/basic/src/utils/strings.ts | 3 + e2e/cases/bundle-false/basic/tsconfig.json | 7 + e2e/cases/bundle-false/index.test.ts | 13 ++ e2e/cases/define/index.test.ts | 12 +- e2e/cases/externals/browser/index.test.ts | 4 +- e2e/cases/externals/node/index.test.ts | 8 +- e2e/cases/syntax/config/index.test.ts | 10 +- e2e/cases/syntax/default/index.test.ts | 10 +- e2e/scripts/shared.ts | 32 +++-- package.json | 3 +- packages/core/package.json | 1 + packages/core/prebundle.config.mjs | 1 + packages/core/src/config.ts | 129 +++++++++++++++++- packages/core/src/types/config/index.ts | 1 + packages/core/src/utils/helper.ts | 25 +++- .../tests/__snapshots__/config.test.ts.snap | 4 + packages/core/tests/helper.test.ts | 27 ++++ pnpm-lock.yaml | 110 ++++++++------- scripts/dictionary.txt | 1 + 28 files changed, 360 insertions(+), 111 deletions(-) create mode 100644 e2e/cases/bundle-false/basic/rslib.config.ts create mode 100644 e2e/cases/bundle-false/basic/src/index.ts create mode 100644 e2e/cases/bundle-false/basic/src/sum.ts create mode 100644 e2e/cases/bundle-false/basic/src/utils/numbers.ts create mode 100644 e2e/cases/bundle-false/basic/src/utils/strings.ts create mode 100644 e2e/cases/bundle-false/basic/tsconfig.json create mode 100644 e2e/cases/bundle-false/index.test.ts create mode 100644 packages/core/tests/helper.test.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index f443a0b3..96c3481e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -37,5 +37,6 @@ }, "[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" - } + }, + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c29e12aa..3b7a1ace 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,6 @@ The project is still under development, so it possible dependents on unstable Rs Current unstable versions are: - +| Package | Link | +| ------------ | ------------------------------------------------------- | +| @rspack/core | [PR](https://github.com/web-infra-dev/rspack/pull/7210) | diff --git a/e2e/cases/alias/__snapshots__/index.test.ts.snap b/e2e/cases/alias/__snapshots__/index.test.ts.snap index bf70ab6f..8f21725b 100644 --- a/e2e/cases/alias/__snapshots__/index.test.ts.snap +++ b/e2e/cases/alias/__snapshots__/index.test.ts.snap @@ -14,8 +14,7 @@ export { a }; `; exports[`source.alias 2`] = ` -"(() => { // webpackBootstrap -"use strict"; +""use strict"; var __webpack_exports__ = {}; ;// CONCATENATED MODULE: ./src/a.ts @@ -28,6 +27,5 @@ console.info(a); var __webpack_export_target__ = exports; for(var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i]; if(__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, '__esModule', { value: true }); -})() -;" +" `; diff --git a/e2e/cases/alias/index.test.ts b/e2e/cases/alias/index.test.ts index 0b4fc036..90ffa190 100644 --- a/e2e/cases/alias/index.test.ts +++ b/e2e/cases/alias/index.test.ts @@ -1,14 +1,14 @@ import { expect, test } from 'vitest'; -import { buildAndGetEntryJsResults } from '#shared'; +import { buildAndGetJsResults } from '#shared'; test('source.alias', async () => { const fixturePath = __dirname; - const { contents } = await buildAndGetEntryJsResults(fixturePath); + const { entries } = await buildAndGetJsResults(fixturePath); - expect(contents.esm).toContain('hello world'); - expect(contents.cjs).toContain('hello world'); + expect(entries.esm).toContain('hello world'); + expect(entries.cjs).toContain('hello world'); // simple artifacts check - expect(contents.esm).toMatchSnapshot(); - expect(contents.cjs).toMatchSnapshot(); + expect(entries.esm).toMatchSnapshot(); + expect(entries.cjs).toMatchSnapshot(); }); diff --git a/e2e/cases/autoExtension/index.test.ts b/e2e/cases/autoExtension/index.test.ts index e026c594..90c79fde 100644 --- a/e2e/cases/autoExtension/index.test.ts +++ b/e2e/cases/autoExtension/index.test.ts @@ -1,17 +1,17 @@ import { extname, join } from 'node:path'; import { expect, test } from 'vitest'; -import { buildAndGetEntryJsResults } from '#shared'; +import { buildAndGetJsResults } from '#shared'; test('autoExtension generate .mjs in build artifacts with esm format when type is commonjs', async () => { const fixturePath = join(__dirname, 'type-commonjs'); - const { files } = await buildAndGetEntryJsResults(fixturePath); - expect(extname(files.esm!)).toEqual('.mjs'); - expect(extname(files.cjs!)).toEqual('.js'); + const { entryFiles } = await buildAndGetJsResults(fixturePath); + expect(extname(entryFiles.esm!)).toEqual('.mjs'); + expect(extname(entryFiles.cjs!)).toEqual('.js'); }); test('autoExtension generate .cjs in build artifacts with cjs format when type is module', async () => { const fixturePath = join(__dirname, 'type-module'); - const { files } = await buildAndGetEntryJsResults(fixturePath); - expect(extname(files.esm!)).toEqual('.js'); - expect(extname(files.cjs!)).toEqual('.cjs'); + const { entryFiles } = await buildAndGetJsResults(fixturePath); + expect(extname(entryFiles.esm!)).toEqual('.js'); + expect(extname(entryFiles.cjs!)).toEqual('.cjs'); }); diff --git a/e2e/cases/bundle-false/basic/rslib.config.ts b/e2e/cases/bundle-false/basic/rslib.config.ts new file mode 100644 index 00000000..df465227 --- /dev/null +++ b/e2e/cases/bundle-false/basic/rslib.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from '@rslib/core'; +import { generateBundleCjsConfig, generateBundleEsmConfig } from '#shared'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig(__dirname, { + bundle: false, + }), + generateBundleCjsConfig(__dirname, { + bundle: false, + }), + ], + source: { + entry: { + main: ['./src/**'], + }, + }, +}); diff --git a/e2e/cases/bundle-false/basic/src/index.ts b/e2e/cases/bundle-false/basic/src/index.ts new file mode 100644 index 00000000..6eb6b6fb --- /dev/null +++ b/e2e/cases/bundle-false/basic/src/index.ts @@ -0,0 +1,3 @@ +export * from './utils/numbers'; +export * from './utils/strings'; +export * from './sum'; diff --git a/e2e/cases/bundle-false/basic/src/sum.ts b/e2e/cases/bundle-false/basic/src/sum.ts new file mode 100644 index 00000000..2759c882 --- /dev/null +++ b/e2e/cases/bundle-false/basic/src/sum.ts @@ -0,0 +1,5 @@ +import { num1, num2, num3 } from './utils/numbers'; +import { str1, str2, str3 } from './utils/strings'; + +export const numSum = num1 + num2 + num3; +export const strSum = str1 + str2 + str3; diff --git a/e2e/cases/bundle-false/basic/src/utils/numbers.ts b/e2e/cases/bundle-false/basic/src/utils/numbers.ts new file mode 100644 index 00000000..c1d2a8cd --- /dev/null +++ b/e2e/cases/bundle-false/basic/src/utils/numbers.ts @@ -0,0 +1,3 @@ +export const num1 = 1; +export const num2 = 2; +export const num3 = 3; diff --git a/e2e/cases/bundle-false/basic/src/utils/strings.ts b/e2e/cases/bundle-false/basic/src/utils/strings.ts new file mode 100644 index 00000000..d35b5a60 --- /dev/null +++ b/e2e/cases/bundle-false/basic/src/utils/strings.ts @@ -0,0 +1,3 @@ +export const str1 = 'str1'; +export const str2 = 'str2'; +export const str3 = 'str3'; diff --git a/e2e/cases/bundle-false/basic/tsconfig.json b/e2e/cases/bundle-false/basic/tsconfig.json new file mode 100644 index 00000000..888d3e46 --- /dev/null +++ b/e2e/cases/bundle-false/basic/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./" + }, + "include": ["src"] +} diff --git a/e2e/cases/bundle-false/index.test.ts b/e2e/cases/bundle-false/index.test.ts new file mode 100644 index 00000000..7cf3c24e --- /dev/null +++ b/e2e/cases/bundle-false/index.test.ts @@ -0,0 +1,13 @@ +import { join } from 'node:path'; +import { expect, test } from 'vitest'; +import { buildAndGetJsResults } from '#shared'; + +test('bundle: false', async () => { + const fixturePath = join(__dirname, 'basic'); + const { files } = await buildAndGetJsResults(fixturePath); + + // TODO: record file paths with inline snapshot + // need to add path serialization + expect(files.esm?.length).toBe(4); + expect(files.cjs?.length).toBe(4); +}); diff --git a/e2e/cases/define/index.test.ts b/e2e/cases/define/index.test.ts index 10a38a80..dadd4d52 100644 --- a/e2e/cases/define/index.test.ts +++ b/e2e/cases/define/index.test.ts @@ -1,13 +1,13 @@ import { expect, test } from 'vitest'; -import { buildAndGetEntryJsResults } from '#shared'; +import { buildAndGetJsResults } from '#shared'; test('source.define', async () => { const fixturePath = __dirname; - const { contents } = await buildAndGetEntryJsResults(fixturePath); + const { entries } = await buildAndGetJsResults(fixturePath); - expect(contents.esm).not.toContain('console.info(VERSION)'); - expect(contents.esm).toContain('1.0.0'); + expect(entries.esm).not.toContain('console.info(VERSION)'); + expect(entries.esm).toContain('1.0.0'); - expect(contents.cjs).not.toContain('console.info(VERSION)'); - expect(contents.cjs).toContain('1.0.0'); + expect(entries.cjs).not.toContain('console.info(VERSION)'); + expect(entries.cjs).toContain('1.0.0'); }); diff --git a/e2e/cases/externals/browser/index.test.ts b/e2e/cases/externals/browser/index.test.ts index 38e1f4d1..b6705851 100644 --- a/e2e/cases/externals/browser/index.test.ts +++ b/e2e/cases/externals/browser/index.test.ts @@ -1,9 +1,9 @@ import { join } from 'node:path'; import { expect, test } from 'vitest'; -import { buildAndGetEntryJsResults } from '#shared'; +import { buildAndGetJsResults } from '#shared'; test('should fail to build when `output.target` is not "node"', async () => { const fixturePath = join(__dirname); - const build = buildAndGetEntryJsResults(fixturePath); + const build = buildAndGetJsResults(fixturePath); await expect(build).rejects.toThrowError('Rspack build failed!'); }); diff --git a/e2e/cases/externals/node/index.test.ts b/e2e/cases/externals/node/index.test.ts index 3ac55f37..423b8c6c 100644 --- a/e2e/cases/externals/node/index.test.ts +++ b/e2e/cases/externals/node/index.test.ts @@ -1,17 +1,17 @@ import { join } from 'node:path'; import { expect, test } from 'vitest'; -import { buildAndGetEntryJsResults } from '#shared'; +import { buildAndGetJsResults } from '#shared'; test('auto externalize Node.js built-in modules when `output.target` is "node"', async () => { const fixturePath = join(__dirname); - const { contents } = await buildAndGetEntryJsResults(fixturePath); + const { entries } = await buildAndGetJsResults(fixturePath); for (const external of [ 'import * as __WEBPACK_EXTERNAL_MODULE_fs__ from "fs"', 'import * as __WEBPACK_EXTERNAL_MODULE_node_assert__ from "node:assert"', 'import * as __WEBPACK_EXTERNAL_MODULE_react__ from "react"', ]) { - expect(contents.esm).toContain(external); + expect(entries.esm).toContain(external); } for (const external of [ @@ -19,6 +19,6 @@ test('auto externalize Node.js built-in modules when `output.target` is "node"', 'var external_node_assert_namespaceObject = require("node:assert");', 'var external_react_namespaceObject = require("react");', ]) { - expect(contents.cjs).toContain(external); + expect(entries.cjs).toContain(external); } }); diff --git a/e2e/cases/syntax/config/index.test.ts b/e2e/cases/syntax/config/index.test.ts index 5967448b..e3e5f714 100644 --- a/e2e/cases/syntax/config/index.test.ts +++ b/e2e/cases/syntax/config/index.test.ts @@ -1,12 +1,12 @@ import { expect, test } from 'vitest'; -import { buildAndGetEntryJsResults } from '#shared'; +import { buildAndGetJsResults } from '#shared'; test('should downgrade class private method by default', async () => { const fixturePath = __dirname; - const { contents } = await buildAndGetEntryJsResults(fixturePath); + const { entries } = await buildAndGetJsResults(fixturePath); - expect(contents.esm).toMatchSnapshot(); - expect(contents.esm).not.toContain('#bar'); + expect(entries.esm).toMatchSnapshot(); + expect(entries.esm).not.toContain('#bar'); - expect(contents.cjs).toContain('#bar'); + expect(entries.cjs).toContain('#bar'); }); diff --git a/e2e/cases/syntax/default/index.test.ts b/e2e/cases/syntax/default/index.test.ts index cb1e7a93..de207958 100644 --- a/e2e/cases/syntax/default/index.test.ts +++ b/e2e/cases/syntax/default/index.test.ts @@ -1,12 +1,12 @@ import { expect, test } from 'vitest'; -import { buildAndGetEntryJsResults } from '#shared'; +import { buildAndGetJsResults } from '#shared'; test('should downgrade class private method by default', async () => { const fixturePath = __dirname; - const { contents } = await buildAndGetEntryJsResults(fixturePath); + const { entries } = await buildAndGetJsResults(fixturePath); - expect(contents.esm).toMatchSnapshot(); - expect(contents.esm).toContain('#bar'); + expect(entries.esm).toMatchSnapshot(); + expect(entries.esm).toContain('#bar'); - expect(contents.cjs).toContain('#bar'); + expect(entries.cjs).toContain('#bar'); }); diff --git a/e2e/scripts/shared.ts b/e2e/scripts/shared.ts index 24e80ddc..3e65866c 100644 --- a/e2e/scripts/shared.ts +++ b/e2e/scripts/shared.ts @@ -37,39 +37,51 @@ export function generateBundleCjsConfig( return mergeConfig(cjsBasicConfig, config)!; } -export async function getEntryJsResults(rslibConfig: RslibConfig) { - const files: Record = {}; - const contents: Record = {}; +export async function getJsResults(rslibConfig: RslibConfig) { + const files: Record = {}; + const contents: Record> = {}; + const entries: Record = {}; + const entryFiles: Record = {}; for (const libConfig of rslibConfig.lib) { - const result = await globContentJSON(libConfig?.output?.distPath?.root!, { + const content = await globContentJSON(libConfig?.output?.distPath?.root!, { absolute: true, ignore: ['/**/*.map'], }); - const entryJs = Object.keys(result).find((file) => + const jsFiles = Object.keys(content).filter((file) => /\.(js|cjs|mjs)$/.test(file), ); - if (entryJs) { - files[libConfig.format!] = entryJs; - contents[libConfig.format!] = result[entryJs]!; + if (jsFiles.length) { + files[libConfig.format!] = jsFiles; + contents[libConfig.format!] = content; + } + + // Only applied in bundle mode, a shortcut to get single entry result + if (libConfig.bundle !== false && jsFiles.length === 1) { + entries[libConfig.format!] = Object.values(content)[0]!; + entryFiles[libConfig.format!] = jsFiles[0]!; } } return { files, contents, + entries, + entryFiles, }; } -export const buildAndGetEntryJsResults = async (fixturePath: string) => { +export const buildAndGetJsResults = async (fixturePath: string) => { const rslibConfig = await loadConfig(join(fixturePath, 'rslib.config.ts')); process.chdir(fixturePath); await build(rslibConfig); - const results = await getEntryJsResults(rslibConfig); + const results = await getJsResults(rslibConfig); return { contents: results.contents, files: results.files, + entries: results.entries, + entryFiles: results.entryFiles, }; }; diff --git a/package.json b/package.json index 1ae78a0b..74b635a6 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@biomejs/biome": "^1.8.3", "@modern-js/module-tools": "^2.55.0", "@types/fs-extra": "^11.0.4", + "@types/node": "~18.19.39", "check-dependency-version-consistency": "^4.1.0", "cross-env": "^7.0.3", "cspell-ban-words": "^0.0.3", @@ -47,7 +48,7 @@ }, "pnpm": { "overrides": { - "@rspack/core": "1.0.0-alpha.3" + "@rspack/core": "npm:@rspack/core-canary@1.0.0-canary-d77b591-20240718094414" } } } diff --git a/packages/core/package.json b/packages/core/package.json index 27b206e6..7b740ad9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -38,6 +38,7 @@ "devDependencies": { "@rslib/tsconfig": "workspace:*", "@types/fs-extra": "^11.0.4", + "fast-glob": "^3.3.2", "commander": "^12.1.0", "fs-extra": "^11.2.0", "picocolors": "1.0.1", diff --git a/packages/core/prebundle.config.mjs b/packages/core/prebundle.config.mjs index b2cf129e..90f1f6f9 100644 --- a/packages/core/prebundle.config.mjs +++ b/packages/core/prebundle.config.mjs @@ -13,6 +13,7 @@ export default { }, dependencies: [ 'commander', + 'fast-glob', { name: 'picocolors', beforeBundle({ depPath }) { diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 73e3f9cb..253db3e5 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -1,11 +1,13 @@ import fs from 'node:fs'; -import { dirname, isAbsolute, join } from 'node:path'; + +import path, { dirname, isAbsolute, join } from 'node:path'; import { type RsbuildConfig, createRsbuild, defineConfig as defineRsbuildConfig, mergeRsbuildConfig, } from '@rsbuild/core'; +import glob from 'fast-glob'; import { DEFAULT_CONFIG_NAME, DEFAULT_EXTENSIONS } from './constant'; import type { Format, @@ -17,6 +19,7 @@ import type { Syntax, } from './types/config'; import { getDefaultExtension } from './utils/extension'; +import { calcLongestCommonPath } from './utils/helper'; import { color } from './utils/helper'; import { nodeBuiltInModules } from './utils/helper'; import { logger } from './utils/logger'; @@ -131,6 +134,7 @@ const getDefaultFormatConfig = (format: Format): RsbuildConfig => { rspack: { externalsType: 'commonjs', output: { + iife: false, chunkFormat: 'commonjs', library: { type: 'commonjs', @@ -177,7 +181,7 @@ const getDefaultAutoExtensionConfig = ( }; }; -const getDefaultSyntax = (syntax?: Syntax): RsbuildConfig => { +const getDefaultSyntaxConfig = (syntax?: Syntax): RsbuildConfig => { // Defaults to ESNext, Rslib will assume all of the latest JavaScript and CSS features are supported. return syntax === undefined ? { @@ -201,6 +205,92 @@ const getDefaultSyntax = (syntax?: Syntax): RsbuildConfig => { }; }; +const getDefaultEntryConfig = async ( + entries: NonNullable['entry'], + libConfig: LibConfig, + root: string, +): Promise => { + if (!entries) { + return {}; + } + + if (libConfig.bundle !== false) { + return { + source: { + entry: entries, + }, + }; + } + + // In bundleless mode, resolve glob patterns and convert them to entry object. + const resolvedEntries: Record = {}; + for (const key of Object.keys(entries)) { + const entry = entries[key]; + + // Entries in bundleless mode could be: + // 1. A string of glob pattern: { entry: { main: 'src/*.ts' } } + // 2. An array of glob patterns: { entry: { main: ['src/*.ts', 'src/*.tsx'] } } + // Not supported for now: entry description object + const entryFiles = Array.isArray(entry) + ? entry + : typeof entry === 'string' + ? [entry] + : null; + + if (!entryFiles) { + throw new Error( + 'Entry can only be a string or an array of strings for now', + ); + } + + // Turn entries in array into each separate entry. + const resolvedEntryFiles = await glob(entryFiles, { + cwd: root, + }); + + if (resolvedEntryFiles.length === 0) { + throw new Error(`Cannot find ${resolvedEntryFiles}`); + } + + // Similar to `rootDir` in tsconfig and `outbase` in esbuild. + const lcp = calcLongestCommonPath(resolvedEntryFiles); + // Using the longest common path of all non-declaration input files by default. + const outBase = lcp === null ? root : lcp; + + for (const file of resolvedEntryFiles) { + const { dir, name } = path.parse(path.relative(outBase, file)); + // Entry filename contains nested path to preserve source directory structure. + const entryFileName = path.join(dir, name); + resolvedEntries[entryFileName] = file; + } + } + + return { + source: { + entry: resolvedEntries, + }, + }; +}; + +const getBundleConfig = (bundle = true): RsbuildConfig => { + if (bundle) return {}; + + return { + output: { + externals: [ + (data: any, callback: any) => { + // Issuer is not empty string when the module is imported by another module. + // Prevent from externalizing entry modules here. + if (data.contextInfo.issuer) { + return callback(null, data.request); + } + callback(); + }, + ], + }, + }; +}; + export function convertLibConfigToRsbuildConfig( libConfig: LibConfig, configPath: string, @@ -213,17 +303,33 @@ export function convertLibConfigToRsbuildConfig( dirname(configPath), autoExtension, ); - const syntaxConfig = getDefaultSyntax(libConfig.output?.syntax); + const syntaxConfig = getDefaultSyntaxConfig(libConfig.output?.syntax); + const bundleConfig = getBundleConfig(libConfig.bundle); - return mergeRsbuildConfig(formatConfig, autoExtensionConfig, syntaxConfig); + return mergeRsbuildConfig( + formatConfig, + autoExtensionConfig, + syntaxConfig, + bundleConfig, + ); } -function postUpdateRsbuildConfig(rsbuildConfig: RsbuildConfig) { +async function postUpdateRsbuildConfig( + libConfig: LibConfig, + rsbuildConfig: RsbuildConfig, + configPath: string, +) { const defaultTargetConfig = getDefaultTargetConfig( rsbuildConfig.output?.target ?? 'web', ); - return mergeRsbuildConfig(defaultTargetConfig); + const defaultEntryConfig = await getDefaultEntryConfig( + rsbuildConfig.source?.entry, + libConfig, + dirname(configPath), + ); + + return mergeRsbuildConfig(defaultTargetConfig, defaultEntryConfig); } const getDefaultTargetConfig = (target: string): RsbuildConfig => { @@ -278,7 +384,16 @@ export async function composeCreateRsbuildConfig( // Some configurations can be defined both in the shared config and the lib config. // So we need to do the post process after lib config is converted and merged. - const postUpdatedConfig = postUpdateRsbuildConfig(mergedRsbuildConfig); + const postUpdatedConfig = await postUpdateRsbuildConfig( + libConfig, + mergedRsbuildConfig, + configPath, + ); + + // Reset some fields as they will be totally overridden by the following merge + // and we don't want to keep them in the final config. + mergedRsbuildConfig.source ??= {}; + mergedRsbuildConfig.source.entry = {}; composedRsbuildConfig[format!] = mergeRsbuildConfig( mergedRsbuildConfig, diff --git a/packages/core/src/types/config/index.ts b/packages/core/src/types/config/index.ts index b31a3052..b57a89d7 100644 --- a/packages/core/src/types/config/index.ts +++ b/packages/core/src/types/config/index.ts @@ -24,6 +24,7 @@ export type Syntax = | string[]; export interface LibConfig extends RsbuildConfig { + bundle?: boolean; format?: Format; autoExtension?: boolean; output?: RsbuildConfig['output'] & { diff --git a/packages/core/src/utils/helper.ts b/packages/core/src/utils/helper.ts index 9eab11a7..e8d8c744 100644 --- a/packages/core/src/utils/helper.ts +++ b/packages/core/src/utils/helper.ts @@ -1,3 +1,4 @@ +import path from 'node:path'; import color from 'picocolors'; /** @@ -66,4 +67,26 @@ export const nodeBuiltInModules = [ 'pnpapi', ]; -export { color }; +function calcLongestCommonPath(absPaths: string[]): string | null { + if (absPaths.length === 0) { + return null; + } + + const splitPaths = absPaths.map((p) => p.split(path.sep)); + let lca = splitPaths[0]!; + for (let i = 1; i < splitPaths.length; i++) { + const currentPath = splitPaths[i]!; + const minLength = Math.min(lca.length, currentPath.length); + + let j = 0; + while (j < minLength && lca[j] === currentPath[j]) { + j++; + } + + lca = lca.slice(0, j); + } + + return lca.length > 0 ? lca.join(path.sep) : '/'; +} + +export { color, calcLongestCommonPath }; diff --git a/packages/core/tests/__snapshots__/config.test.ts.snap b/packages/core/tests/__snapshots__/config.test.ts.snap index 3576c0d6..7866268d 100644 --- a/packages/core/tests/__snapshots__/config.test.ts.snap +++ b/packages/core/tests/__snapshots__/config.test.ts.snap @@ -21,6 +21,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1 "bar": "bar/cjs", "foo": "foo", }, + "entry": {}, "preEntry": [ "./a.js", "./c.js", @@ -40,6 +41,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1 "externalsType": "commonjs", "output": { "chunkFormat": "commonjs", + "iife": false, "library": { "type": "commonjs", }, @@ -68,6 +70,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1 "bar": "bar", "foo": "foo/esm", }, + "entry": {}, "preEntry": "./b.js", }, "tools": { @@ -116,6 +119,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1 "bar": "bar", "foo": "foo", }, + "entry": {}, "preEntry": "./a.js", }, "tools": { diff --git a/packages/core/tests/helper.test.ts b/packages/core/tests/helper.test.ts new file mode 100644 index 00000000..f01c1dab --- /dev/null +++ b/packages/core/tests/helper.test.ts @@ -0,0 +1,27 @@ +import { join } from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { calcLongestCommonPath } from '../src/utils/helper'; + +describe('LCA calculate correctly', () => { + it('empty array', () => { + const result = calcLongestCommonPath([]); + expect(result).toBe(null); + }); + + it('correct 1', () => { + const result = calcLongestCommonPath([ + '/Users/Someone/project-a/src/helpers', + '/Users/Someone/project-a/src', + '/Users/Someone/project-a/src/utils', + ]); + expect(result).toBe('/Users/Someone/project-a/src'); + }); + + it('correct 2', () => { + const result = calcLongestCommonPath([ + '/Users/Someone/project-monorepo/packages-a/src', + '/Users/Someone/project-monorepo/packages-util/src', + ]); + expect(result).toBe('/Users/Someone/project-monorepo'); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3b19ff0..a9d28f8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: - '@rspack/core': 1.0.0-alpha.3 + '@rspack/core': npm:@rspack/core-canary@1.0.0-canary-d77b591-20240718094414 importers: @@ -20,6 +20,9 @@ importers: '@types/fs-extra': specifier: ^11.0.4 version: 11.0.4 + '@types/node': + specifier: ~18.19.39 + version: 18.19.39 check-dependency-version-consistency: specifier: ^4.1.0 version: 4.1.0 @@ -113,6 +116,9 @@ importers: commander: specifier: ^12.1.0 version: 12.1.0 + fast-glob: + specifier: ^3.3.2 + version: 3.3.2 fs-extra: specifier: ^11.2.0 version: 11.2.0 @@ -1119,56 +1125,56 @@ packages: engines: {node: '>=16.7.0'} hasBin: true - '@rspack/binding-darwin-arm64@1.0.0-alpha.3': - resolution: {integrity: sha512-PZLdp0tgoti/skzIMijNr2jedKa8LGbhtPs6a0jgIuLY1g0fj/aL3LLGMo4rwoy/zGXeZf40PIJQB8b+w0qt7g==} + '@rspack/binding-canary@1.0.0-canary-d77b591-20240718094414': + resolution: {integrity: sha512-0DTf4WeXs8mcoZ8maeuD8kZCnPmvZxY8VCED3bL5oMRM8VT9ons8dggn1UFDBaiUoqyD7Ko2OFKb3n1VYHboYw==} + + '@rspack/binding-darwin-arm64-canary@1.0.0-canary-d77b591-20240718094414': + resolution: {integrity: sha512-7Drn6VzjpkBvmeMmV+SjZFN1IxtiNvVGzzLERH3SuRyYT6lv/WthgiOocCtcPQxTbCbBS67NGTglPndWZIOuDg==} cpu: [arm64] os: [darwin] - '@rspack/binding-darwin-x64@1.0.0-alpha.3': - resolution: {integrity: sha512-NrNfjzsWo3kFh37tpCxNw75xuSGHdGCHIRCjKnvxHQ46aB+Y2wiOdGgSk7SnZHsRWpZyDFw3aBJCayiXlfgdTw==} + '@rspack/binding-darwin-x64-canary@1.0.0-canary-d77b591-20240718094414': + resolution: {integrity: sha512-XfBueryD7RHweNJlpXbk0+mswCJFhiey3Cw+RZuO8SJnur+u66ePeH2OF3Atuk8ToKMsXEX81vkcGrZ2LwntIg==} cpu: [x64] os: [darwin] - '@rspack/binding-linux-arm64-gnu@1.0.0-alpha.3': - resolution: {integrity: sha512-EjzyZWZSjo02ReGUzQPt8sY1hEx2V9lEbg1cqgnE1NpSOS77ratNoAvS3gAzXL6NGWRhYrIH2yaN+6OB9olt/g==} + '@rspack/binding-linux-arm64-gnu-canary@1.0.0-canary-d77b591-20240718094414': + resolution: {integrity: sha512-6f5OSeprOAx0fdyKAdtUnZJvI1NoesPZtcutbuounYr8EBnWU9OcC795iSzLj9lsbsZ6ZcWwaRb45vLcJRd9vw==} cpu: [arm64] os: [linux] - '@rspack/binding-linux-arm64-musl@1.0.0-alpha.3': - resolution: {integrity: sha512-HJQ52KWNnMOFqbXhaIHTAr54ES5LSunJF6SLnIMgElReC39WvUNDmHhCA5yPebkXgY2SDrLIKDmqxouZmYWulQ==} + '@rspack/binding-linux-arm64-musl-canary@1.0.0-canary-d77b591-20240718094414': + resolution: {integrity: sha512-Ajx3GNSOO5eeth9FLY9DMmLEaTiijT5SybTV2nibzb5DLXz41ZjNWCiK2VqlJAf7nodF9QZPsEO/T3V0Ky6Q7Q==} cpu: [arm64] os: [linux] - '@rspack/binding-linux-x64-gnu@1.0.0-alpha.3': - resolution: {integrity: sha512-VcpKLI2AZmFOTec8C9YJTdMrgZMrgsQkMeQzTY1uOQuIaAaNCuPBFRdlJaRSTAG0t4aaxaVfR1c3JY8GITacfA==} + '@rspack/binding-linux-x64-gnu-canary@1.0.0-canary-d77b591-20240718094414': + resolution: {integrity: sha512-nkdF/E3h8H9dKGKmIOkEv+yn8i7gH9E2WRcfsBdNxHAxS+XUEZFc9xNbSsk6auf/1Crznb+umc6hvQ5nQrTUDQ==} cpu: [x64] os: [linux] - '@rspack/binding-linux-x64-musl@1.0.0-alpha.3': - resolution: {integrity: sha512-FypR+RqONTvrgX+SI8sJqhVqv8uhTdq3OHew4ZaL3VN0dp2thmpMX5cJ+XQAsU414OLRTgREU9go2j78n7kvUA==} + '@rspack/binding-linux-x64-musl-canary@1.0.0-canary-d77b591-20240718094414': + resolution: {integrity: sha512-Cpezgks1pBD1ZBfi3g2Lz8u3LCYVa/jfnrOxEzs0KXaFskKtehZpJQBFx4m0iOaJRIVL/CEz3P8yhbU0Uzhi4g==} cpu: [x64] os: [linux] - '@rspack/binding-win32-arm64-msvc@1.0.0-alpha.3': - resolution: {integrity: sha512-aAGQE2TJOhlK6jGYXZyon0JKTP5t2o51/exsSzyH6BSqFce/Qd5w1fqgm6FONTuosrwaBxHSz1pprNq6vx87kA==} + '@rspack/binding-win32-arm64-msvc-canary@1.0.0-canary-d77b591-20240718094414': + resolution: {integrity: sha512-iSHftYqDPMQOoNTN0Rnadi0cYnvJGSGNuHB0rhnRJysFlkwQWdwLnJXHigqwxQQT0a2fSjuUdeXnobf8f0uMwQ==} cpu: [arm64] os: [win32] - '@rspack/binding-win32-ia32-msvc@1.0.0-alpha.3': - resolution: {integrity: sha512-OouAliQG6dONL9B+Jy237fhs6bScloAT3uphkDumsiAmH1926MYeSKhsmYU4j8b352iGtZVXaH/wR5svLTUCVQ==} + '@rspack/binding-win32-ia32-msvc-canary@1.0.0-canary-d77b591-20240718094414': + resolution: {integrity: sha512-edtlZ9NnufE2DKVcQgMpTNTCjp8BDsHgWfN6YsoJjzjHJvtWiubShnp2/1wrwxmbYzCS09w9v6+Sy8apRz6OeA==} cpu: [ia32] os: [win32] - '@rspack/binding-win32-x64-msvc@1.0.0-alpha.3': - resolution: {integrity: sha512-CLh3p5a15wPQE8zOyBjBjVBrbdaDvAfacXkmCQK4I6lBc3HetkJNyjS8Fntf1CV9sWgJhwABUWHn7kMQeLY9RQ==} + '@rspack/binding-win32-x64-msvc-canary@1.0.0-canary-d77b591-20240718094414': + resolution: {integrity: sha512-q8+CKpTbwtXDYnWIsLlMKGjO00pEOHBhbhmOrcO0qKHrVGBP5brb6WA37tYAN330fErsLT0FTppa3LV8Vd0nCQ==} cpu: [x64] os: [win32] - '@rspack/binding@1.0.0-alpha.3': - resolution: {integrity: sha512-S/JjBWr8PE/l7+2xsk1m77CZnKwQNk+39uIsvHQhoRs+DL9SUDjjkUO4yqjCw6ZUGqEaTv4U/TL9TAmbrTth7g==} - - '@rspack/core@1.0.0-alpha.3': - resolution: {integrity: sha512-TcZZNMpyTjEIBP4zMCpLwXBiATEQ2QG3jKsV+mq55ZGKfqd/l86Zr3SboF15GOQip1wDHlSpA1bvrT18f9h0sw==} + '@rspack/core-canary@1.0.0-canary-d77b591-20240718094414': + resolution: {integrity: sha512-JgmIq1GUdi8e93dQBI1JQFKg3szLBhar1lZCodH3Mqyht+C0wGSRaoy+QegHLkgpx+00qUWLDf3M3eLP4lqntA==} engines: {node: '>=16.0.0'} peerDependencies: '@swc/helpers': '>=0.5.1' @@ -1176,6 +1182,10 @@ packages: '@swc/helpers': optional: true + '@rspack/lite-tapable-canary@1.0.0-canary-d77b591-20240718094414': + resolution: {integrity: sha512-rTGAkDCbq7pyVV2BhkYx0xgK65XEhv4VAkZB5HBhbs2HeB2qkP0yT8NZEgkAtMg5R6Q54dndeZGLFboqYtlF5w==} + engines: {node: '>=16.0.0'} + '@rspack/lite-tapable@1.0.0-alpha.3': resolution: {integrity: sha512-oQJ1iYxfBHcuutAva2HP1dqi9Aka/70PB3Vbq4nI+iAhHErtzaRslI/OcqhEbbmBgYf+Xu6g5vvN6Gxfq69gag==} engines: {node: '>=16.0.0'} @@ -3537,7 +3547,7 @@ snapshots: '@rsbuild/core@1.0.0-alpha.9': dependencies: - '@rspack/core': 1.0.0-alpha.3(@swc/helpers@0.5.11) + '@rspack/core': '@rspack/core-canary@1.0.0-canary-d77b591-20240718094414(@swc/helpers@0.5.11)' '@rspack/lite-tapable': 1.0.0-alpha.3 '@swc/helpers': 0.5.11 caniuse-lite: 1.0.30001641 @@ -3546,54 +3556,56 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - '@rspack/binding-darwin-arm64@1.0.0-alpha.3': + '@rspack/binding-canary@1.0.0-canary-d77b591-20240718094414': + optionalDependencies: + '@rspack/binding-darwin-arm64': '@rspack/binding-darwin-arm64-canary@1.0.0-canary-d77b591-20240718094414' + '@rspack/binding-darwin-x64': '@rspack/binding-darwin-x64-canary@1.0.0-canary-d77b591-20240718094414' + '@rspack/binding-linux-arm64-gnu': '@rspack/binding-linux-arm64-gnu-canary@1.0.0-canary-d77b591-20240718094414' + '@rspack/binding-linux-arm64-musl': '@rspack/binding-linux-arm64-musl-canary@1.0.0-canary-d77b591-20240718094414' + '@rspack/binding-linux-x64-gnu': '@rspack/binding-linux-x64-gnu-canary@1.0.0-canary-d77b591-20240718094414' + '@rspack/binding-linux-x64-musl': '@rspack/binding-linux-x64-musl-canary@1.0.0-canary-d77b591-20240718094414' + '@rspack/binding-win32-arm64-msvc': '@rspack/binding-win32-arm64-msvc-canary@1.0.0-canary-d77b591-20240718094414' + '@rspack/binding-win32-ia32-msvc': '@rspack/binding-win32-ia32-msvc-canary@1.0.0-canary-d77b591-20240718094414' + '@rspack/binding-win32-x64-msvc': '@rspack/binding-win32-x64-msvc-canary@1.0.0-canary-d77b591-20240718094414' + + '@rspack/binding-darwin-arm64-canary@1.0.0-canary-d77b591-20240718094414': optional: true - '@rspack/binding-darwin-x64@1.0.0-alpha.3': + '@rspack/binding-darwin-x64-canary@1.0.0-canary-d77b591-20240718094414': optional: true - '@rspack/binding-linux-arm64-gnu@1.0.0-alpha.3': + '@rspack/binding-linux-arm64-gnu-canary@1.0.0-canary-d77b591-20240718094414': optional: true - '@rspack/binding-linux-arm64-musl@1.0.0-alpha.3': + '@rspack/binding-linux-arm64-musl-canary@1.0.0-canary-d77b591-20240718094414': optional: true - '@rspack/binding-linux-x64-gnu@1.0.0-alpha.3': + '@rspack/binding-linux-x64-gnu-canary@1.0.0-canary-d77b591-20240718094414': optional: true - '@rspack/binding-linux-x64-musl@1.0.0-alpha.3': + '@rspack/binding-linux-x64-musl-canary@1.0.0-canary-d77b591-20240718094414': optional: true - '@rspack/binding-win32-arm64-msvc@1.0.0-alpha.3': + '@rspack/binding-win32-arm64-msvc-canary@1.0.0-canary-d77b591-20240718094414': optional: true - '@rspack/binding-win32-ia32-msvc@1.0.0-alpha.3': + '@rspack/binding-win32-ia32-msvc-canary@1.0.0-canary-d77b591-20240718094414': optional: true - '@rspack/binding-win32-x64-msvc@1.0.0-alpha.3': + '@rspack/binding-win32-x64-msvc-canary@1.0.0-canary-d77b591-20240718094414': optional: true - '@rspack/binding@1.0.0-alpha.3': - optionalDependencies: - '@rspack/binding-darwin-arm64': 1.0.0-alpha.3 - '@rspack/binding-darwin-x64': 1.0.0-alpha.3 - '@rspack/binding-linux-arm64-gnu': 1.0.0-alpha.3 - '@rspack/binding-linux-arm64-musl': 1.0.0-alpha.3 - '@rspack/binding-linux-x64-gnu': 1.0.0-alpha.3 - '@rspack/binding-linux-x64-musl': 1.0.0-alpha.3 - '@rspack/binding-win32-arm64-msvc': 1.0.0-alpha.3 - '@rspack/binding-win32-ia32-msvc': 1.0.0-alpha.3 - '@rspack/binding-win32-x64-msvc': 1.0.0-alpha.3 - - '@rspack/core@1.0.0-alpha.3(@swc/helpers@0.5.11)': + '@rspack/core-canary@1.0.0-canary-d77b591-20240718094414(@swc/helpers@0.5.11)': dependencies: '@module-federation/runtime-tools': 0.2.3 - '@rspack/binding': 1.0.0-alpha.3 - '@rspack/lite-tapable': 1.0.0-alpha.3 + '@rspack/binding': '@rspack/binding-canary@1.0.0-canary-d77b591-20240718094414' + '@rspack/lite-tapable': '@rspack/lite-tapable-canary@1.0.0-canary-d77b591-20240718094414' caniuse-lite: 1.0.30001641 optionalDependencies: '@swc/helpers': 0.5.11 + '@rspack/lite-tapable-canary@1.0.0-canary-d77b591-20240718094414': {} + '@rspack/lite-tapable@1.0.0-alpha.3': {} '@sinclair/typebox@0.27.8': {} diff --git a/scripts/dictionary.txt b/scripts/dictionary.txt index 5952268f..6685defc 100644 --- a/scripts/dictionary.txt +++ b/scripts/dictionary.txt @@ -60,6 +60,7 @@ onclosetag onopentag ontext osascript +outbase outro pageerror pathinfo