diff --git a/examples/basic-project/src/pages/index.module.scss b/examples/basic-project/src/pages/index.module.scss index 71f0de02af..999a61e098 100644 --- a/examples/basic-project/src/pages/index.module.scss +++ b/examples/basic-project/src/pages/index.module.scss @@ -1,3 +1,5 @@ +@import "var.scss"; + .data { margin-top: 10px; } \ No newline at end of file diff --git a/examples/basic-project/src/pages/var.scss b/examples/basic-project/src/pages/var.scss new file mode 100644 index 0000000000..c9c817a79d --- /dev/null +++ b/examples/basic-project/src/pages/var.scss @@ -0,0 +1 @@ +$color-1 : #fff; \ No newline at end of file diff --git a/packages/bundles/CHANGELOG.md b/packages/bundles/CHANGELOG.md new file mode 100644 index 0000000000..f43350a721 --- /dev/null +++ b/packages/bundles/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +## 0.1.1 + +- [fix] Bump version of `@swc/core`(1.3.3 -> 1.3.19), https://github.com/swc-project/swc/issues/6371 +- [fix] Bump version of `caniuse-lite`(^1.0.30001332 -> ^1.0.30001431), postcss requires version upper than 1.0.30001400 +- [fix] Bump version of `webpack`(5.74.0 -> 5.75.0), minor version update +- [add] Add `@ice/swc-plugin-remove-export`(0.1.2) +- [add] Add `@ice/swc-plugin-keep-export`(0.1.3) +- [add] Add `@ice/swc-plugin-keep-platform`(0.1.2) +- [add] Add `core-js`(3.26.0) diff --git a/packages/bundles/package.json b/packages/bundles/package.json index d7dfdf39f7..2ca90806d4 100644 --- a/packages/bundles/package.json +++ b/packages/bundles/package.json @@ -1,6 +1,6 @@ { "name": "@ice/bundles", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "author": "ICE", "description": "Basic dependencies for ice.", @@ -15,10 +15,14 @@ "main": "./esm/index.js", "type": "module", "dependencies": { + "@swc/core": "1.3.19", + "@ice/swc-plugin-remove-export": "0.1.2", + "@ice/swc-plugin-keep-export": "0.1.3", + "@ice/swc-plugin-keep-platform": "0.1.2", "ansi-html-community": "^0.0.8", "html-entities": "^2.3.2", - "@swc/core": "1.3.3", - "caniuse-lite": "^1.0.30001332", + "core-js": "3.26.0", + "caniuse-lite": "^1.0.30001431", "chokidar": "3.5.3", "events": "3.3.0", "jest-worker": "27.5.1", @@ -61,7 +65,7 @@ "terser-webpack-plugin": "5.3.5", "typescript": "^4.6.4", "trusted-cert": "1.1.3", - "webpack": "5.74.0", + "webpack": "5.75.0", "webpack-bundle-analyzer": "4.5.0", "webpack-dev-server": "4.10.0", "unplugin": "0.9.5", diff --git a/packages/bundles/src/index.ts b/packages/bundles/src/index.ts index 4e3d9b550f..37918ca381 100644 --- a/packages/bundles/src/index.ts +++ b/packages/bundles/src/index.ts @@ -1,12 +1,32 @@ -// reexport for dependencies +import { createRequire } from 'module'; +import { dirname } from 'path'; import postcss from 'postcss'; import less from 'less'; import sass from 'sass'; +import swc from '@swc/core'; + +const require = createRequire(import.meta.url); +const swcPluginRemoveExport = require.resolve('@ice/swc-plugin-remove-export'); +const swcPluginKeepExport = require.resolve('@ice/swc-plugin-keep-export'); +const swcPluginKeepPlatform = require.resolve('@ice/swc-plugin-keep-platform'); +const coreJsPath = dirname(require.resolve('core-js/package.json')); export { postcss, less, sass, + + swc, + swcPluginRemoveExport, + swcPluginKeepExport, + swcPluginKeepPlatform, + + coreJsPath, }; export type { ProcessOptions } from 'postcss'; +export type { + Options as SwcConfig, + Config as SwcCompilationConfig, + ReactConfig, +} from '@swc/core'; diff --git a/packages/ice/CHANGELOG.md b/packages/ice/CHANGELOG.md index 203bcc34fa..c83e5186ba 100644 --- a/packages/ice/CHANGELOG.md +++ b/packages/ice/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## v3.0.1 + +- [fix] file resolve when use css module in server compiling +- [fix] lock version of `@ice/bundles`, `@ice/webpack-config` and `@ice/route-manifest` +- [feat] support external config for server +- [feat] redirect imports for data loader + ## v3.0.0 -- [feat] provider basic service for web framework `ice` \ No newline at end of file +- [feat] provider basic service for web framework `ice` diff --git a/packages/ice/package.json b/packages/ice/package.json index b003d9d274..e876aa0006 100644 --- a/packages/ice/package.json +++ b/packages/ice/package.json @@ -1,6 +1,6 @@ { "name": "@ice/app", - "version": "3.0.0", + "version": "3.0.1", "description": "provide scripts and configuration used by web framework ice", "type": "module", "main": "./esm/index.js", @@ -29,18 +29,18 @@ }, "author": "ice-admin", "license": "MIT", - "repository": "ice-lab/ice-next", - "bugs": "https://github.com/ice-lab/ice-next/issues", - "homepage": "https://next.ice.work", + "repository": "alibaba/ice", + "bugs": "https://github.com/alibaba/ice/issues", + "homepage": "https://v3.ice.work", "dependencies": { "@babel/generator": "7.18.10", "@babel/parser": "7.18.10", "@babel/traverse": "7.18.10", "@babel/types": "7.18.10", - "@ice/bundles": "^0.1.0", - "@ice/route-manifest": "^1.0.0", + "@ice/bundles": "0.1.1", + "@ice/route-manifest": "1.0.0", "@ice/runtime": "^1.0.0", - "@ice/webpack-config": "^1.0.0", + "@ice/webpack-config": "1.0.1", "@swc/helpers": "0.4.12", "@types/express": "^4.17.14", "address": "^1.1.2", diff --git a/packages/ice/src/createService.ts b/packages/ice/src/createService.ts index b3a310b000..c050359768 100644 --- a/packages/ice/src/createService.ts +++ b/packages/ice/src/createService.ts @@ -43,14 +43,17 @@ interface CreateServiceOptions { async function createService({ rootDir, command, commandArgs }: CreateServiceOptions) { const buildSpinner = createSpinner('loading config...'); - const templateDir = path.join(__dirname, '../templates/core/'); - const configFile = 'ice.config.(mts|mjs|ts|js|cjs|json)'; + const templateDir = path.join(__dirname, '../templates/'); + const coreTemplate = path.join(templateDir, 'core/'); + const configFile = commandArgs.config || 'ice.config.(mts|mjs|ts|js|cjs|json)'; const dataCache = new Map(); const generator = new Generator({ + // Directory of templates includes `core` and `exports`. + templateDir, rootDir, targetDir: RUNTIME_TMP_DIR, // add default template of ice - templates: [templateDir], + templates: [coreTemplate], }); const { addWatchEvent, removeWatchEvent } = createWatch({ @@ -159,13 +162,11 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt const platformTaskConfig = taskConfigs[0]; const iceRuntimePath = '@ice/runtime'; - const enableRoutes = platform === WEB; // add render data generator.setRenderData({ ...routesInfo, platform, iceRuntimePath, - enableRoutes, hasExportAppData, runtimeModules, coreEnvKeys, @@ -173,6 +174,8 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt memoryRouter: platformTaskConfig.config.memoryRouter, hydrate: !csr, importCoreJs: polyfill === 'entry', + // Enable react-router for web as default. + enableRoutes: true, }); dataCache.set('routes', JSON.stringify(routesInfo)); dataCache.set('hasExportAppData', hasExportAppData ? 'true' : ''); @@ -184,7 +187,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt }, generator.addRenderFile, { rootDir, runtimeDir: RUNTIME_TMP_DIR, - templateDir: path.join(templateDir, '../exports'), + templateDir: path.join(templateDir, 'exports'), dataLoader: userConfig.dataLoader, }); @@ -207,10 +210,9 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt ...getWatchEvents({ generator, targetDir: RUNTIME_TMP_DIR, - templateDir, + templateDir: coreTemplate, cache: dataCache, ctx, - serverCompiler, }), ); diff --git a/packages/ice/src/esbuild/cssModules.ts b/packages/ice/src/esbuild/cssModules.ts index 069a690823..85e18a8296 100644 --- a/packages/ice/src/esbuild/cssModules.ts +++ b/packages/ice/src/esbuild/cssModules.ts @@ -1,4 +1,5 @@ import * as path from 'path'; +import url from 'url'; import fse from 'fs-extra'; import temp from 'temp'; import cssModules from '@ice/bundles/compiled/postcss-modules/index.js'; @@ -90,11 +91,15 @@ async function parseStyle(filePath: string) { } if (ext === '.scss') { - return (await sass.compileStringAsync(content)).css; + return (await sass.compileStringAsync(content, { + url: url.pathToFileURL(filePath), + })).css; } if (ext === '.less') { - return (await less.render(content)).css; + return (await less.render(content, { + filename: filePath, + })).css; } throw new Error(`Can't parse the style '${ext}'.`); diff --git a/packages/ice/src/esbuild/alias.ts b/packages/ice/src/esbuild/resolve.ts similarity index 58% rename from packages/ice/src/esbuild/alias.ts rename to packages/ice/src/esbuild/resolve.ts index 374787aa23..0420d09144 100644 --- a/packages/ice/src/esbuild/alias.ts +++ b/packages/ice/src/esbuild/resolve.ts @@ -8,18 +8,23 @@ interface PluginOptions { alias: Record; externalDependencies: boolean; format: BuildOptions['format']; + externals?: string[]; } -const aliasPlugin = (options: PluginOptions): Plugin => { - const { alias, externalDependencies, format } = options; +const resolvePlugin = (options: PluginOptions): Plugin => { + const { alias, externalDependencies, format, externals } = options; return { - name: 'esbuild-alias', + name: 'esbuild-resolve', setup(build: PluginBuild) { build.onResolve({ filter: /.*/ }, (args) => { const id = args.path; + // ice do not support alias with config onlyModule const resolved = resolveId(id, alias); + if (resolved && resolved !== id) { + let resolvedPath = resolved; + // glob specified file module aliased with absolute path if (!path.extname(resolved) && path.isAbsolute(resolved)) { const basename = path.basename(resolved); @@ -28,26 +33,53 @@ const aliasPlugin = (options: PluginOptions): Plugin => { cwd: path.dirname(resolved), absolute: true, })[0]; + if (absoluteId) { - return { - path: absoluteId, - }; + resolvedPath = absoluteId; } } - return { path: resolved }; + + const external = shouldExternal(resolvedPath, externals); + + // absolute path can not be resolved by other plugins, so we need to check external here. + if (external) { + return { + path: resolvedPath, + external: true, + }; + } + + return { path: resolvedPath }; } - }); - build.onResolve({ filter: /.*/ }, (args) => { - const id = args.path; + // external ids which is third-party dependencies if (id[0] !== '.' && !path.isAbsolute(id) && externalDependencies && isExternalBuiltinDep(id, format)) { return { external: true, }; } + + const external = shouldExternal(id, externals); + + if (external) { + return { + path: id, + external: true, + }; + } }); }, }; }; -export default aliasPlugin; +function shouldExternal(id: string, externals?: string[]) { + if (!externals) { + return; + } + + return externals.find(regexStr => { + return new RegExp(regexStr).test(id); + }); +} + +export default resolvePlugin; diff --git a/packages/ice/src/getWatchEvents.ts b/packages/ice/src/getWatchEvents.ts index ac8a45550e..c73377d489 100644 --- a/packages/ice/src/getWatchEvents.ts +++ b/packages/ice/src/getWatchEvents.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import consola from 'consola'; import type { Context } from 'build-scripts'; import type { Config } from '@ice/webpack-config/esm/types'; -import type { ServerCompiler, WatchEvent } from './types/plugin.js'; +import type { WatchEvent } from './types/plugin.js'; import { generateRoutesInfo } from './routes.js'; import type Generator from './service/runtimeGenerator'; import getGlobalStyleGlobPattern from './utils/getGlobalStyleGlobPattern.js'; @@ -15,7 +15,6 @@ interface Options { generator: Generator; cache: Map; ctx: Context; - serverCompiler: ServerCompiler; } const getWatchEvents = (options: Options): WatchEvent[] => { diff --git a/packages/ice/src/service/config.ts b/packages/ice/src/service/config.ts index 12584f9ecd..7e9e606856 100644 --- a/packages/ice/src/service/config.ts +++ b/packages/ice/src/service/config.ts @@ -7,7 +7,7 @@ import { getCache, setCache } from '../utils/persistentCache.js'; import { getFileHash } from '../utils/hash.js'; import dynamicImport from '../utils/dynamicImport.js'; import formatPath from '../utils/formatPath.js'; -import { RUNTIME_TMP_DIR } from '../constant.js'; +import { RUNTIME_TMP_DIR, CACHE_DIR } from '../constant.js'; type GetOutfile = (entry: string, exportNames: string[]) => string; @@ -115,7 +115,7 @@ export const getAppExportConfig = (rootDir: string) => { } const appEntry = path.join(rootDir, 'src/app'); const getOutfile = (entry: string, keepExports: string[]) => - formatPath(path.join(rootDir, 'node_modules', `${keepExports.join('_')}_${path.basename(entry)}.mjs`)); + formatPath(path.join(rootDir, CACHE_DIR, `${keepExports.join('_')}_${path.basename(entry)}.mjs`)); const config = new Config({ entry: appEntry, rootDir, diff --git a/packages/ice/src/service/preBundleCJSDeps.ts b/packages/ice/src/service/preBundleCJSDeps.ts index 68ef741176..d100ddcd7e 100644 --- a/packages/ice/src/service/preBundleCJSDeps.ts +++ b/packages/ice/src/service/preBundleCJSDeps.ts @@ -13,7 +13,7 @@ import flattenId from '../utils/flattenId.js'; import formatPath from '../utils/formatPath.js'; import { BUILDIN_CJS_DEPS, BUILDIN_ESM_DEPS } from '../constant.js'; import type { DepScanData } from '../esbuild/scan.js'; -import aliasPlugin from '../esbuild/alias.js'; +import resolvePlugin from '../esbuild/resolve.js'; import emptyCSSPlugin from '../esbuild/emptyCSS.js'; import cssModulesPlugin from '../esbuild/cssModules.js'; import escapeLocalIdent from '../utils/escapeLocalIdent.js'; @@ -96,7 +96,7 @@ export default async function preBundleCJSDeps(options: PreBundleDepsOptions): P ignoreAnnotations: true, plugins: [ emptyCSSPlugin(), - aliasPlugin({ alias, format: 'cjs', externalDependencies: false }), + resolvePlugin({ alias, format: 'cjs', externalDependencies: false }), cssModulesPlugin({ extract: false, generateLocalIdentName: function (name: string, filename: string) { diff --git a/packages/ice/src/service/runtimeGenerator.ts b/packages/ice/src/service/runtimeGenerator.ts index 5eae2fe3b9..97fdce4ae5 100644 --- a/packages/ice/src/service/runtimeGenerator.ts +++ b/packages/ice/src/service/runtimeGenerator.ts @@ -29,6 +29,7 @@ const { debounce } = lodash; const RENDER_WAIT = 150; interface Options { + templateDir: string; rootDir: string; targetDir: string; defaultRenderData?: RenderData; @@ -102,6 +103,8 @@ export function removeDeclarations(exportList: DeclarationData[], removeSource: export default class Generator { private targetDir: string; + private templateDir: string; + private renderData: RenderData; private contentRegistration: Registration; @@ -117,9 +120,10 @@ export default class Generator { private contentTypes: string[]; public constructor(options: Options) { - const { rootDir, targetDir, defaultRenderData = {}, templates } = options; + const { rootDir, targetDir, defaultRenderData = {}, templates, templateDir } = options; this.rootDir = rootDir; this.targetDir = targetDir; + this.templateDir = templateDir; this.renderData = defaultRenderData; this.contentRegistration = {}; this.rerender = false; @@ -243,8 +247,7 @@ export default class Generator { templates.forEach((templateFile) => { const templatePath = path.isAbsolute(templateFile) ? templateFile : path.join(template, templateFile); const filePath = path.isAbsolute(templateFile) ? path.basename(templateFile) : templateFile; - const targetPath = path.join(this.targetDir, targetDir, filePath); - + const targetPath = path.join(targetDir, filePath); this.addRenderFile(templatePath, targetPath, extraData); }); if (this.rerender) { @@ -261,11 +264,14 @@ export default class Generator { public renderFile: RenderFile = (templatePath, targetPath, extraData = {}) => { const renderExt = '.ejs'; - const realTargetPath = path.isAbsolute(targetPath) ? targetPath : path.join(this.rootDir, targetPath); + const realTargetPath = path.isAbsolute(targetPath) + ? targetPath : path.join(this.rootDir, this.targetDir, targetPath); // example: templatePath = 'routes.ts.ejs' + const realTemplatePath = path.isAbsolute(templatePath) + ? templatePath : path.join(this.templateDir, templatePath); const { ext } = path.parse(templatePath); if (ext === renderExt) { - const templateContent = fse.readFileSync(templatePath, 'utf-8'); + const templateContent = fse.readFileSync(realTemplatePath, 'utf-8'); let renderData = { ...this.renderData }; if (typeof extraData === 'function') { renderData = extraData(this.renderData); @@ -279,7 +285,7 @@ export default class Generator { fse.writeFileSync(realTargetPath.replace(renderExt, ''), content, 'utf-8'); } else { fse.ensureDirSync(path.dirname(realTargetPath)); - fse.copyFileSync(templatePath, realTargetPath); + fse.copyFileSync(realTemplatePath, realTargetPath); } }; } diff --git a/packages/ice/src/service/serverCompiler.ts b/packages/ice/src/service/serverCompiler.ts index 33bb3462da..0eff8827dd 100644 --- a/packages/ice/src/service/serverCompiler.ts +++ b/packages/ice/src/service/serverCompiler.ts @@ -1,6 +1,8 @@ import * as path from 'path'; import consola from 'consola'; import esbuild from 'esbuild'; +import fse from 'fs-extra'; +import fg from 'fast-glob'; import type { Config } from '@ice/webpack-config/esm/types'; import lodash from '@ice/bundles/compiled/lodash/index.js'; import type { TaskConfig } from 'build-scripts'; @@ -9,7 +11,7 @@ import type { ServerCompiler } from '../types/plugin.js'; import type { UserConfig } from '../types/userConfig.js'; import escapeLocalIdent from '../utils/escapeLocalIdent.js'; import cssModulesPlugin from '../esbuild/cssModules.js'; -import aliasPlugin from '../esbuild/alias.js'; +import resolvePlugin from '../esbuild/resolve.js'; import ignorePlugin from '../esbuild/ignore.js'; import createAssetsPlugin from '../esbuild/assets.js'; import { CACHE_DIR, SERVER_OUTPUT_DIR } from '../constant.js'; @@ -19,6 +21,7 @@ import transformPipePlugin from '../esbuild/transformPipe.js'; import isExternalBuiltinDep from '../utils/isExternalBuiltinDep.js'; import getServerEntry from '../utils/getServerEntry.js'; import type { DepScanData } from '../esbuild/scan.js'; +import formatPath from '../utils/formatPath.js'; import { scanImports } from './analyze.js'; import type { DepsMetaData } from './preBundleCJSDeps.js'; import preBundleCJSDeps from './preBundleCJSDeps.js'; @@ -31,7 +34,8 @@ interface Options { syntaxFeatures: UserConfig['syntaxFeatures']; } -const { merge } = lodash; +const { merge, difference } = lodash; + export function createServerCompiler(options: Options) { const { task, rootDir, command, server, syntaxFeatures } = options; @@ -53,6 +57,8 @@ export function createServerCompiler(options: Options) { externalDependencies, transformEnv = true, assetsManifest, + redirectImports, + removeOutputs, } = {}) => { let depsMetadata: DepsMetaData; let swcOptions = merge({}, { @@ -70,6 +76,7 @@ export function createServerCompiler(options: Options) { env: false, polyfill: false, swcOptions, + redirectImports, }, 'esbuild'); if (preBundle) { @@ -113,17 +120,21 @@ export function createServerCompiler(options: Options) { // while it is not recommended loader: { '.js': 'jsx' }, jsx: 'automatic', - sourcemap: sourceMap ? 'inline' : false, + sourcemap: typeof sourceMap === 'boolean' + // Transform sourceMap for esbuild. + ? sourceMap : (sourceMap.includes('inline') ? 'inline' : !!sourceMap), ...customBuildOptions, define, + absWorkingDir: rootDir, external: Object.keys(externals), plugins: [ ...(customBuildOptions.plugins || []), emptyCSSPlugin(), - aliasPlugin({ + resolvePlugin({ alias, externalDependencies: externalDependencies ?? !server.bundle, format, + externals: server.externals, }), server?.ignores && ignorePlugin(server.ignores), cssModulesPlugin({ @@ -145,7 +156,6 @@ export function createServerCompiler(options: Options) { ].filter(Boolean), }), ].filter(Boolean), - }; if (typeof task.config?.server?.buildOptions === 'function') { buildOptions = task.config.server.buildOptions(buildOptions); @@ -163,6 +173,15 @@ export function createServerCompiler(options: Options) { const outJSExtension = esm ? '.mjs' : '.cjs'; const serverEntry = path.join(rootDir, task.config.outputDir, SERVER_OUTPUT_DIR, `index${outJSExtension}`); + if (removeOutputs && esbuildResult.metafile) { + // build/server/a.mjs -> a.mjs + const currentOutputFiles = Object.keys(esbuildResult.metafile.outputs) + .map(output => output.replace(formatPath(`${path.relative(rootDir, buildOptions.outdir)}${path.sep}`), '')); + const allOutputFiles = fg.sync('**', { cwd: buildOptions.outdir }); + const outdatedFiles = difference(allOutputFiles, currentOutputFiles); + outdatedFiles.forEach(outdatedFile => fse.removeSync(path.join(buildOptions.outdir, outdatedFile))); + } + return { ...esbuildResult, serverEntry, diff --git a/packages/ice/src/types/plugin.ts b/packages/ice/src/types/plugin.ts index 2e72466bcb..b556bde238 100644 --- a/packages/ice/src/types/plugin.ts +++ b/packages/ice/src/types/plugin.ts @@ -27,7 +27,8 @@ type ServerCompilerBuildOptions = Pick< 'outExtension' | 'plugins' | 'logLevel' | - 'sourcemap' + 'sourcemap' | + 'metafile' >; export type ServerCompiler = ( @@ -38,6 +39,8 @@ export type ServerCompiler = ( externalDependencies?: boolean; transformEnv?: boolean; assetsManifest?: AssetsManifest; + redirectImports?: Config['redirectImports']; + removeOutputs?: boolean; } ) => Promise>; export type WatchEvent = [ diff --git a/packages/ice/src/types/userConfig.ts b/packages/ice/src/types/userConfig.ts index d90a3a9f76..5d8f14664a 100644 --- a/packages/ice/src/types/userConfig.ts +++ b/packages/ice/src/types/userConfig.ts @@ -55,6 +55,7 @@ export interface UserConfig { format?: 'esm' | 'cjs'; bundle?: boolean; ignores?: IgnorePattern[]; + externals?: string[]; }; optimization?: Optimization; mock?: { exclude?: string[] }; diff --git a/packages/ice/src/utils/getServerCompilerPlugin.ts b/packages/ice/src/utils/getServerCompilerPlugin.ts index 341947a138..36045628cf 100644 --- a/packages/ice/src/utils/getServerCompilerPlugin.ts +++ b/packages/ice/src/utils/getServerCompilerPlugin.ts @@ -30,6 +30,7 @@ function getServerCompilerPlugin(serverCompiler: ServerCompiler, options: Option format, platform: isEsm ? 'browser' : 'node', outExtension: { '.js': isEsm ? '.mjs' : '.cjs' }, + metafile: true, }, { preBundle: format === 'esm' && (ssr || ssg), @@ -40,6 +41,7 @@ function getServerCompilerPlugin(serverCompiler: ServerCompiler, options: Option return getRoutePathsFromCache(dataCache); }, }, + removeOutputs: true, }, ], ensureRoutesConfig, diff --git a/packages/ice/src/webpack/DataLoaderPlugin.ts b/packages/ice/src/webpack/DataLoaderPlugin.ts index 7bfb164bd3..0b3c5d17db 100644 --- a/packages/ice/src/webpack/DataLoaderPlugin.ts +++ b/packages/ice/src/webpack/DataLoaderPlugin.ts @@ -67,6 +67,11 @@ export default class DataLoaderPlugin { preBundle: false, externalDependencies: false, transformEnv: false, + // Redirect import defineDataLoader from @ice/runtime to avoid build plugin side effect code. + redirectImports: [{ + specifier: ['defineDataLoader'], + source: '@ice/runtime', + }], }, ); if (error) { diff --git a/packages/ice/tests/transformImport.test.ts b/packages/ice/tests/transformImport.test.ts index 1d165b1499..3dc5504eb1 100644 --- a/packages/ice/tests/transformImport.test.ts +++ b/packages/ice/tests/transformImport.test.ts @@ -7,7 +7,7 @@ import { createUnplugin } from 'unplugin'; import preBundleCJSDeps from '../src/service/preBundleCJSDeps'; import { scanImports } from '../src/service/analyze'; import transformImport from '../src/esbuild/transformImport'; -import aliasPlugin from '../src/esbuild/alias'; +import resolvePlugin from '../src/esbuild/resolve'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const alias = { '@': path.join(__dirname, './fixtures/scan') }; @@ -29,7 +29,7 @@ it('transform module import', async () => { entryPoints: [appEntry], outdir, plugins: [ - aliasPlugin({ alias, format: 'esm', externalDependencies: false }), + resolvePlugin({ alias, format: 'esm', externalDependencies: false }), transformImportPlugin(), ], }); diff --git a/packages/miniapp-html-styles/package.json b/packages/miniapp-html-styles/package.json index 68da74f61c..142bd520ac 100644 --- a/packages/miniapp-html-styles/package.json +++ b/packages/miniapp-html-styles/package.json @@ -13,8 +13,8 @@ ], "author": "ICE", "license": "MIT", - "repository": "ice-lab/ice-next", - "bugs": "https://github.com/ice-lab/ice-next/issues", - "homepage": "https://next.ice.work", + "repository": "alibaba/ice", + "bugs": "https://github.com/alibaba/ice/issues", + "homepage": "https://v3.ice.work", "sideEffects": false } diff --git a/packages/miniapp-loader/package.json b/packages/miniapp-loader/package.json index e385e8d245..1bf19835f3 100644 --- a/packages/miniapp-loader/package.json +++ b/packages/miniapp-loader/package.json @@ -9,9 +9,9 @@ ], "author": "ICE", "license": "MIT", - "repository": "ice-lab/ice-next", - "bugs": "https://github.com/ice-lab/ice-next/issues", - "homepage": "https://next.ice.work", + "repository": "alibaba/ice", + "bugs": "https://github.com/alibaba/ice/issues", + "homepage": "https://v3.ice.work", "scripts": { "watch": "tsc -w", "build": "tsc" diff --git a/packages/miniapp-react-dom/package.json b/packages/miniapp-react-dom/package.json index 1f74c450dc..2b5ac1656e 100644 --- a/packages/miniapp-react-dom/package.json +++ b/packages/miniapp-react-dom/package.json @@ -14,9 +14,9 @@ ], "author": "ICE", "license": "MIT", - "repository": "ice-lab/ice-next", - "bugs": "https://github.com/ice-lab/ice-next/issues", - "homepage": "https://next.ice.work", + "repository": "alibaba/ice", + "bugs": "https://github.com/alibaba/ice/issues", + "homepage": "https://v3.ice.work", "scripts": { "watch": "tsc -w", "build": "tsc" diff --git a/packages/miniapp-runtime/package.json b/packages/miniapp-runtime/package.json index 02f40153d7..3acd6b0020 100644 --- a/packages/miniapp-runtime/package.json +++ b/packages/miniapp-runtime/package.json @@ -15,9 +15,9 @@ ], "author": "ICE", "license": "MIT", - "repository": "ice-lab/ice-next", - "bugs": "https://github.com/ice-lab/ice-next/issues", - "homepage": "https://next.ice.work", + "repository": "alibaba/ice", + "bugs": "https://github.com/alibaba/ice/issues", + "homepage": "https://v3.ice.work", "scripts": { "watch": "tsc -w", "build": "tsc" diff --git a/packages/plugin-antd/package.json b/packages/plugin-antd/package.json index 3dbc6730a0..2e304df08a 100644 --- a/packages/plugin-antd/package.json +++ b/packages/plugin-antd/package.json @@ -13,7 +13,7 @@ "license": "MIT", "repository": { "type": "http", - "url": "https://github.com/ice-lab/ice-next/tree/master/packages/plugin-antd" + "url": "https://github.com/alibaba/ice/tree/master/packages/plugin-antd" }, "devDependencies": { "@ice/app": "^3.0.0" diff --git a/packages/plugin-auth/package.json b/packages/plugin-auth/package.json index d6f95cb295..441364b20d 100644 --- a/packages/plugin-auth/package.json +++ b/packages/plugin-auth/package.json @@ -50,7 +50,7 @@ }, "repository": { "type": "http", - "url": "https://github.com/ice-lab/ice-next/tree/master/packages/plugin-auth" + "url": "https://github.com/alibaba/ice/tree/master/packages/plugin-auth" }, "scripts": { "watch": "tsc -w", diff --git a/packages/plugin-css-assets-local/package.json b/packages/plugin-css-assets-local/package.json index f2e1d1bdc8..7192961400 100644 --- a/packages/plugin-css-assets-local/package.json +++ b/packages/plugin-css-assets-local/package.json @@ -4,7 +4,7 @@ "license": "MIT", "repository": { "type": "http", - "url": "https://github.com/ice-lab/ice-next/tree/master/packages/plugin-css-assets-local" + "url": "https://github.com/alibaba/ice/tree/master/packages/plugin-css-assets-local" }, "type": "module", "files": [ @@ -31,4 +31,4 @@ "watch": "tsc -w", "build": "tsc" } -} \ No newline at end of file +} diff --git a/packages/plugin-fusion/package.json b/packages/plugin-fusion/package.json index d675838f7c..87aa13f666 100644 --- a/packages/plugin-fusion/package.json +++ b/packages/plugin-fusion/package.json @@ -18,7 +18,7 @@ }, "repository": { "type": "http", - "url": "https://github.com/ice-lab/ice-next/tree/master/packages/plugin-fusion" + "url": "https://github.com/alibaba/ice/tree/master/packages/plugin-fusion" }, "publishConfig": { "access": "public" diff --git a/packages/plugin-jsx-plus/package.json b/packages/plugin-jsx-plus/package.json index 37946afad4..97f772bc95 100644 --- a/packages/plugin-jsx-plus/package.json +++ b/packages/plugin-jsx-plus/package.json @@ -34,7 +34,7 @@ }, "repository": { "type": "http", - "url": "https://github.com/ice-lab/ice-next/tree/master/packages/plugin-jsx-plus" + "url": "https://github.com/alibaba/ice/tree/master/packages/plugin-jsx-plus" }, "scripts": { "watch": "tsc -w", diff --git a/packages/plugin-miniapp/package.json b/packages/plugin-miniapp/package.json index b4fb8a3b0e..013a83de87 100644 --- a/packages/plugin-miniapp/package.json +++ b/packages/plugin-miniapp/package.json @@ -35,6 +35,6 @@ }, "repository": { "type": "http", - "url": "https://github.com/ice-lab/ice-next/tree/master/packages/plugin-miniapp" + "url": "https://github.com/alibaba/ice/tree/master/packages/plugin-miniapp" } } diff --git a/packages/plugin-miniapp/src/index.ts b/packages/plugin-miniapp/src/index.ts index 1201d107e6..0f6f2959d4 100644 --- a/packages/plugin-miniapp/src/index.ts +++ b/packages/plugin-miniapp/src/index.ts @@ -24,24 +24,26 @@ const plugin: Plugin = (miniappOptions = {}) => ({ // Recommand add @ice/miniapp-runtime in dependencies when use pnpm. // Use `@ice/miniapp-runtime/esm/app` for vscode type hint. const miniappRuntime = '@ice/miniapp-runtime/esm/app'; - generator.addExport({ - specifier: [ - 'defineAppConfig', - 'useAppData', - 'useData', - 'useConfig', - 'Link', - 'useSearchParams', - 'history', - 'defineDataLoader', - ], - source: miniappRuntime, + const importSpecifiers = [ + 'defineAppConfig', + 'useAppData', + 'useData', + 'useConfig', + 'Link', + 'useSearchParams', + 'history', + 'defineDataLoader', + ]; + generator.addRenderFile('core/entry.client.tsx.ejs', 'entry.miniapp.tsx', { + iceRuntimePath: miniappRuntime, + enableRoutes: false, }); - generator.modifyRenderData((renderData) => { - return { - ...renderData, - iceRuntimePath: miniappRuntime, - }; + + generator.addRenderFile('core/index.ts.ejs', 'index.miniapp.ts', { + framework: { + imports: `import { ${importSpecifiers.join(',\n')} } from '${miniappRuntime}';`, + exports: importSpecifiers.join(',\n'), + }, }); // Get server compiler by hooks onHook(`before.${command as 'start' | 'build'}.run`, async ({ getAppConfig, getRoutesConfig }) => { diff --git a/packages/plugin-miniapp/src/miniapp/index.ts b/packages/plugin-miniapp/src/miniapp/index.ts index 0f5d504b89..c2c5045b77 100644 --- a/packages/plugin-miniapp/src/miniapp/index.ts +++ b/packages/plugin-miniapp/src/miniapp/index.ts @@ -22,7 +22,7 @@ function getEntry(rootDir: string, runtimeDir: string) { })[0]; if (!entryFile) { // use generated file in template directory - entryFile = path.join(rootDir, runtimeDir, 'entry.client.tsx'); + entryFile = path.join(rootDir, runtimeDir, 'entry.miniapp.tsx'); } return { main: entryFile, @@ -66,7 +66,7 @@ const getMiniappTask = ({ cacheDir: path.join(rootDir, 'node_modules', '.cache'), sourceMap: command === 'start' ? 'cheap-module-source-map' : false, alias: { - ice: path.join(rootDir, runtimeDir, 'index.ts'), + ice: path.join(rootDir, runtimeDir, 'index.miniapp.ts'), '@': path.join(rootDir, 'src'), // 小程序使用 regenerator-runtime@0.11 'regenerator-runtime': require.resolve('regenerator-runtime'), diff --git a/packages/plugin-moment-locales/package.json b/packages/plugin-moment-locales/package.json index c78247f9c7..ddd6cae7a9 100644 --- a/packages/plugin-moment-locales/package.json +++ b/packages/plugin-moment-locales/package.json @@ -13,7 +13,7 @@ "license": "MIT", "repository": { "type": "http", - "url": "https://github.com/ice-lab/ice-next/tree/master/packages/plugin-moment-locales" + "url": "https://github.com/alibaba/ice/tree/master/packages/plugin-moment-locales" }, "devDependencies": { "@ice/app": "^3.0.0" diff --git a/packages/plugin-pha/CHANGELOG.md b/packages/plugin-pha/CHANGELOG.md index a2f29d3327..19dc931866 100644 --- a/packages/plugin-pha/CHANGELOG.md +++ b/packages/plugin-pha/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.0.1 + +- [fix] failed to get route config when re-define route path + ## 1.0.0 - [feat] plugin to enable PHA features \ No newline at end of file diff --git a/packages/plugin-pha/package.json b/packages/plugin-pha/package.json index 3f6103dd91..92d3c2a045 100644 --- a/packages/plugin-pha/package.json +++ b/packages/plugin-pha/package.json @@ -1,6 +1,6 @@ { "name": "@ice/plugin-pha", - "version": "1.0.0", + "version": "1.0.1", "description": "ice.js plugin for PHA.", "license": "MIT", "type": "module", @@ -16,6 +16,7 @@ "build": "tsc" }, "dependencies": { + "@remix-run/router": "^1.0.3", "chalk": "^4.0.0", "consola": "^2.15.3", "humps": "^2.0.1", @@ -29,6 +30,6 @@ }, "repository": { "type": "http", - "url": "https://github.com/ice-lab/ice-next/tree/master/packages/plugin-pha" + "url": "https://github.com/alibaba/ice/tree/master/packages/plugin-pha" } } diff --git a/packages/plugin-pha/src/index.ts b/packages/plugin-pha/src/index.ts index 282b4ce3a6..5f53d203be 100644 --- a/packages/plugin-pha/src/index.ts +++ b/packages/plugin-pha/src/index.ts @@ -40,6 +40,9 @@ const plugin: Plugin = (options) => ({ type: true, source: '@ice/plugin-pha/esm/types', }); + + // TODO: get route manifest by API. + const routeManifest = path.join(rootDir, '.ice', 'route-manifest.json'); // Get server compiler by hooks onHook(`before.${command as 'start' | 'build'}.run`, async ({ serverCompiler, taskConfigs, urls, ...restAPI }) => { const taskConfig = taskConfigs.find(({ name }) => name === 'web').config; @@ -79,6 +82,7 @@ const plugin: Plugin = (options) => ({ urlPrefix, serverEntry: serverEntryRef.current, template, + routeManifest, }, }); }); @@ -126,6 +130,7 @@ const plugin: Plugin = (options) => ({ publicPath, urlPrefix, template, + routeManifest, }, }); diff --git a/packages/plugin-pha/src/manifestHelpers.ts b/packages/plugin-pha/src/manifestHelpers.ts index dee9df192e..8230848139 100644 --- a/packages/plugin-pha/src/manifestHelpers.ts +++ b/packages/plugin-pha/src/manifestHelpers.ts @@ -4,6 +4,7 @@ import * as fs from 'fs'; import humps from 'humps'; import consola from 'consola'; import cloneDeep from 'lodash.clonedeep'; +import { matchRoutes } from '@remix-run/router'; import { decamelizeKeys, camelizeKeys, validPageConfigKeys } from './constants.js'; import type { Page, PHAPage, PageHeader, PageConfig, Manifest, PHAManifest } from './types'; @@ -17,6 +18,7 @@ interface TransformOptions { export interface ParseOptions { urlPrefix: string; publicPath: string; + routeManifest: string; routesConfig?: Record; serverEntry: string; template?: boolean; @@ -94,8 +96,20 @@ function getPageUrl(routeId: string, options: ParseOptions) { return `${urlPrefix}${splitCharacter}${routeId}${urlSuffix}`; } -async function getPageConfig(routeId: string, routesConfig: Record): Promise { - const routeConfig = routesConfig![`${routeId}`]?.() as MixedPage || {}; +async function getPageConfig(routeId: string, routeManifest: string, routesConfig: Record): Promise { + const routes = fs.readFileSync(routeManifest, 'utf-8'); + const matches = matchRoutes(JSON.parse(routes), routeId.startsWith('/') ? routeId : `/${routeId}`); + let routeConfig: MixedPage = {}; + if (matches) { + // Merge route config when return muitiple route. + routeConfig = matches.reduce((prev, curr) => { + const { id } = curr.route; + return { + ...prev, + ...(routesConfig![id]?.() as MixedPage || {}), + }; + }, {}); + } const filteredConfig = {}; Object.keys(routeConfig).forEach((key) => { if (validPageConfigKeys.includes(key)) { @@ -121,11 +135,11 @@ async function renderPageDocument(routeId: string, serverEntry: string): Promise } async function getPageManifest(page: string | Page, options: ParseOptions): Promise { - const { template, serverEntry, routesConfig } = options; + const { template, serverEntry, routesConfig, routeManifest } = options; // Page will be type string when it is a source frame. if (typeof page === 'string') { // Get html content by render document. - const pageConfig = await getPageConfig(page, routesConfig); + const pageConfig = await getPageConfig(page, routeManifest, routesConfig); const { queryParams = '', ...rest } = pageConfig; const pageManifest = { key: page, @@ -303,4 +317,4 @@ export function getMultipleManifest(manifest: PHAManifest): Record { urlPrefix: 'https://url-prefix.com/', routesConfig: (await import(path.join(__dirname, './mockConfig.mjs')))?.default, serverEntry: path.join(__dirname, './mockServer.mjs'), + routeManifest: path.join(__dirname, './route-manifest.json'), }; it('should add urlPrefix to manifest', async () => { @@ -453,6 +454,7 @@ describe('get multiple manifest', async () => { urlPrefix: 'https://url-prefix.com/', routesConfig: (await import(path.join(__dirname, './mockConfig.mjs')))?.default, serverEntry: path.join(__dirname, './mockServer.mjs'), + routeManifest: path.join(__dirname, './route-manifest.json'), }; it('simple routes', async () => { diff --git a/packages/plugin-pha/tests/route-manifest.json b/packages/plugin-pha/tests/route-manifest.json new file mode 100644 index 0000000000..b6a2c3d50f --- /dev/null +++ b/packages/plugin-pha/tests/route-manifest.json @@ -0,0 +1,14 @@ +[ + { + "path": "home", + "id": "home" + }, + { + "path": "about", + "id": "about" + }, + { + "path": "app/nest", + "id": "app/nest" + } +] \ No newline at end of file diff --git a/packages/plugin-rax-compat/package.json b/packages/plugin-rax-compat/package.json index 4266465f8e..27f8b5cc75 100644 --- a/packages/plugin-rax-compat/package.json +++ b/packages/plugin-rax-compat/package.json @@ -33,7 +33,7 @@ }, "repository": { "type": "http", - "url": "https://github.com/ice-lab/ice-next/tree/master/packages/plugin-rax-compat" + "url": "https://github.com/alibaba/ice/tree/master/packages/plugin-rax-compat" }, "scripts": { "watch": "tsc -w", diff --git a/packages/plugin-request/package.json b/packages/plugin-request/package.json index d1b8993f40..cc0b243de1 100644 --- a/packages/plugin-request/package.json +++ b/packages/plugin-request/package.json @@ -64,7 +64,7 @@ }, "repository": { "type": "http", - "url": "https://github.com/ice-lab/ice-next/tree/master/packages/plugin-request" + "url": "https://github.com/alibaba/ice/tree/master/packages/plugin-request" }, "scripts": { "watch": "tsc -w", diff --git a/packages/plugin-store/package.json b/packages/plugin-store/package.json index d11ed5e85a..091dfecf0d 100644 --- a/packages/plugin-store/package.json +++ b/packages/plugin-store/package.json @@ -58,10 +58,10 @@ }, "repository": { "type": "http", - "url": "https://github.com/ice-lab/ice-next/tree/master/packages/plugin-store" + "url": "https://github.com/alibaba/ice/tree/master/packages/plugin-store" }, "scripts": { "watch": "tsc -w", "build": "tsc" } -} \ No newline at end of file +} diff --git a/packages/rax-compat/package.json b/packages/rax-compat/package.json index 8ed02cff78..0fe7960129 100644 --- a/packages/rax-compat/package.json +++ b/packages/rax-compat/package.json @@ -67,9 +67,9 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/ice-lab/ice-next.git" + "url": "git+https://github.com/alibaba/ice.git" }, "author": "ice-admin@alibaba-inc.com", "license": "MIT", - "homepage": "https://github.com/ice-lab/ice-next#readme" -} \ No newline at end of file + "homepage": "https://github.com/alibaba/ice#readme" +} diff --git a/packages/route-manifest/package.json b/packages/route-manifest/package.json index 5c20da8d61..54e3ada409 100644 --- a/packages/route-manifest/package.json +++ b/packages/route-manifest/package.json @@ -11,9 +11,9 @@ "!esm/**/*.map" ], "license": "MIT", - "repository": "ice-lab/ice-next", - "bugs": "https://github.com/ice-lab/ice-next/issues", - "homepage": "https://next.ice.work", + "repository": "alibaba/ice", + "bugs": "https://github.com/alibaba/ice/issues", + "homepage": "https://v3.ice.work", "dependencies": { "minimatch": "^5.0.1" }, diff --git a/packages/runtime/CHANGELOG.md b/packages/runtime/CHANGELOG.md index b3aa418a95..7be827585c 100644 --- a/packages/runtime/CHANGELOG.md +++ b/packages/runtime/CHANGELOG.md @@ -1,5 +1,9 @@ # @ice/runtime +## v1.0.1 + +- [fix] merge window context + ## v1.0.0 - [feat] runtime core for `ice` \ No newline at end of file diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 813628eae8..509b7e0468 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@ice/runtime", - "version": "1.0.0", + "version": "1.0.1", "description": "", "type": "module", "types": "./esm/index.d.ts", @@ -23,9 +23,9 @@ ], "author": "ICE", "license": "MIT", - "repository": "ice-lab/ice-next", - "bugs": "https://github.com/ice-lab/ice-next/issues", - "homepage": "https://next.ice.work", + "repository": "alibaba/ice", + "bugs": "https://github.com/alibaba/ice/issues", + "homepage": "https://v3.ice.work", "scripts": { "watch": "tsc -w", "build": "tsc" diff --git a/packages/runtime/src/Document.tsx b/packages/runtime/src/Document.tsx index deb56d6941..7d6370b86b 100644 --- a/packages/runtime/src/Document.tsx +++ b/packages/runtime/src/Document.tsx @@ -118,11 +118,11 @@ export function Data() { }; return ( - // Disable hydration warning for csr. - // initial app data may not equal csr result. + // Disable hydration warning for csr, initial app data may not equal csr result. + // Should merge global context when there are multiple .