From b1e4ae80c4356e1f648b80bd36aeb0a93d1eaa0e Mon Sep 17 00:00:00 2001 From: #FF0000 Date: Fri, 10 Jan 2025 16:51:15 +0800 Subject: [PATCH] fix(formula): fix formula facade import (#4438) --- CONTRIBUTING.md | 9 ++ examples/esbuild.config.ts | 106 ++++++++++++++++++ examples/package.json | 3 + examples/src/sheets/main.ts | 1 + examples/src/sheets/styles.ts | 32 ++++++ package.json | 1 + packages/engine-formula/src/index.ts | 1 - .../facade/src/apis/__tests__/facade.spec.ts | 6 +- .../sheets-formula/src/facade/f-formula.ts | 11 +- pnpm-lock.yaml | 43 ++++--- 10 files changed, 193 insertions(+), 20 deletions(-) create mode 100644 examples/src/sheets/styles.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 182752fcb55..0c50b4676a8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -197,6 +197,15 @@ and then run the following command to run E2E tests: pnpm test:e2e ``` +### Build Preview + +After building, the output may differ from the source code. To test for any differences, you can link to the built artifacts using: + +```shell +pnpm build +pnpm dev:libs +``` + ### Update Snapshots Univer uses Playwright to perform visual comparison tests. If you have made changes to the UI, the CI may fail due to visual differences. You can update the snapshots by running this GitHub Action [📸 Manually Update Snapshots · Workflow runs · dream-num/univer (github.com)](https://github.com/dream-num/univer/actions/workflows/update-snapshots-manually.yml) on your branch. diff --git a/examples/esbuild.config.ts b/examples/esbuild.config.ts index 75060e7b40c..4a493f397f7 100644 --- a/examples/esbuild.config.ts +++ b/examples/esbuild.config.ts @@ -24,9 +24,12 @@ import cleanPlugin from 'esbuild-plugin-clean'; import copyPlugin from 'esbuild-plugin-copy'; import vue from 'esbuild-plugin-vue3'; import stylePlugin from 'esbuild-style-plugin'; +import glob from 'fast-glob'; +import fs from 'fs-extra'; import minimist from 'minimist'; import tailwindcss from 'tailwindcss'; +const LINK_TO_LIB = process.env.LINK_TO_LIB === 'true'; const nodeModules = path.resolve(process.cwd(), './node_modules'); const args = minimist(process.argv.slice(2)); @@ -41,6 +44,104 @@ const monacoEditorEntryPoints = [ 'vs/editor/editor.worker.js', ]; +/** + * fix `import '@univerjs/xxx/lib/index.css'` not work on source code dev mode + * + * The `stylePlugin` must be loaded after the `skipLibCssEsbuildPlugin`. + */ +const skipLibCssEsbuildPlugin = { + name: 'skip-lib-css-esbuild-plugin', + setup(build) { + console.log('[skip-lib-css-esbuild-plugin] enabled, resolve will skip `import \'@univerjs/xxx/lib/**/*.css\'`'); + + build.onResolve({ filter: /\/lib\/.*\.css$/ }, async (args) => { + if (args.path.includes('@univerjs/')) { + return { + path: args.path, + namespace: 'univer-lib-css', + }; + } + }); + + build.onLoad({ filter: /.*/, namespace: 'univer-lib-css' }, async () => { + // return virtual css content + return { + contents: '', + loader: 'css', + }; + }); + }, +}; + +/** + * Add this function to generate aliases + */ +function generateAliases() { + const aliases = {}; + const packagesRoots = ['packages', 'packages-experimental'].map((dir) => + path.resolve(__dirname, '..', dir) + ); + + for (const packagesRoot of packagesRoots) { + if (!fs.existsSync(packagesRoot)) continue; + + // Find all package.json files in subdirectories + const packageJsonPaths = glob.sync('*/package.json', { cwd: packagesRoot }); + + for (const packageJsonPath of packageJsonPaths) { + const pkgDir = path.join(packagesRoot, path.dirname(packageJsonPath)); + const pkgJson = fs.readJSONSync(path.join(packagesRoot, packageJsonPath)); + const exportsConfig = pkgJson.publishConfig?.exports; + + if (!exportsConfig) continue; + + // Add main package alias + if (exportsConfig['.']) { + aliases[pkgJson.name] = path.resolve(pkgDir, exportsConfig['.'].import); + } + + const getValue = (val: { import: string } | string) => { + if (typeof val === 'string') { + return val; + } + return val.import; + }; + // Add subpath aliases + Object.entries(exportsConfig as Record).forEach(([key, value]) => { + try { + if (key === '.') { + aliases[pkgJson.name] = path.resolve(pkgDir, exportsConfig['.'].import); + } else if (key === './lib/*') { + const cssFile = path.resolve(pkgDir, getValue(value).replace('*', 'index.css')); + if (fs.existsSync(cssFile)) { + aliases[`${pkgJson.name}/lib/index.css`] = cssFile; + } + } else if (key === './*') { + // do nothing + } else if (key === './locale/*') { + const locales = ['en-US', 'fr-FR', 'ru-RU', 'zh-CN', 'zh-TW', 'vi-VN', 'fa-IR']; + locales.forEach((lang) => { + aliases[`${pkgJson.name}/locale/${lang}`] = path.resolve(pkgDir, getValue(value).replace('*', lang)); + }); + } else { + const aliasKey = `${pkgJson.name}/${key.replace('./', '')}`; + const aliasPath = path.resolve(pkgDir, getValue(value)); + aliases[aliasKey] = aliasPath; + } + } catch (e) { + console.error(`Error generating aliases for ${pkgJson.name}: ${e.message}`); + process.exit(1); + } + }); + } + } + + return aliases; +} + +/** + * Build monaco editor's resources for web worker + */ function monacoBuildTask() { return esbuild.build({ entryPoints: monacoEditorEntryPoints.map((entry) => `./node_modules/monaco-editor/esm/${entry}`), @@ -141,6 +242,7 @@ const config: SameShape = { to: ['./'], }, }), + ...(LINK_TO_LIB ? [] : [skipLibCssEsbuildPlugin]), stylePlugin({ postcss: { plugins: [tailwindcss], @@ -160,8 +262,12 @@ const config: SameShape = { entryPoints, outdir: './local', define, + alias: LINK_TO_LIB ? generateAliases() : {}, }; +/** + * Build the project + */ async function main() { if (args.watch) { const ctx = await esbuild.context(config); diff --git a/examples/package.json b/examples/package.json index 0354579a5c1..2e81a2079ee 100644 --- a/examples/package.json +++ b/examples/package.json @@ -8,6 +8,7 @@ "prepare": "tsx ./scripts/generate-locales.ts && tsx ./scripts/generate-html.ts", "build:demo": "tsx ./esbuild.config.ts", "dev:demo": "tsx ./esbuild.config.ts --watch", + "dev:demo-libs": "cross-env LINK_TO_LIB=true tsx ./esbuild.config.ts --watch", "dev:e2e": "tsx ./esbuild.config.ts --watch --e2e", "build:e2e": "tsx ./esbuild.config.ts --e2e", "lint:types": "tsc --noEmit" @@ -78,12 +79,14 @@ "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@univerjs-infra/shared": "workspace:*", + "cross-env": "^7.0.3", "detect-port": "^1.6.1", "esbuild": "^0.24.2", "esbuild-plugin-clean": "^1.0.1", "esbuild-plugin-copy": "^2.1.1", "esbuild-plugin-vue3": "^0.4.2", "esbuild-style-plugin": "^1.6.3", + "fast-glob": "^3.3.3", "fs-extra": "^11.2.0", "less": "^4.2.1", "minimist": "^1.2.8", diff --git a/examples/src/sheets/main.ts b/examples/src/sheets/main.ts index 24449fbe6c2..f7a5f7e89e2 100644 --- a/examples/src/sheets/main.ts +++ b/examples/src/sheets/main.ts @@ -60,6 +60,7 @@ import '@univerjs/sheets-find-replace/facade'; import '@univerjs/sheets-drawing-ui/facade'; import '@univerjs/sheets-zen-editor/facade'; import '../global.css'; +import './styles'; /* eslint-disable-next-line node/prefer-global/process */ const IS_E2E: boolean = !!process.env.IS_E2E; diff --git a/examples/src/sheets/styles.ts b/examples/src/sheets/styles.ts new file mode 100644 index 00000000000..556b3a46151 --- /dev/null +++ b/examples/src/sheets/styles.ts @@ -0,0 +1,32 @@ +/** + * Copyright 2023-present DreamNum Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import '@univerjs/sheets-ui/lib/index.css'; +import '@univerjs/ui/lib/index.css'; +import '@univerjs/design/lib/index.css'; +import '@univerjs/docs-ui/lib/index.css'; +import '@univerjs/sheets-formula-ui/lib/index.css'; +import '@univerjs/sheets-numfmt-ui/lib/index.css'; +import '@univerjs/drawing-ui/lib/index.css'; +import '@univerjs/docs-drawing-ui/lib/index.css'; +import '@univerjs/sheets-data-validation-ui/lib/index.css'; +import '@univerjs/sheets-filter-ui/lib/index.css'; +import '@univerjs/find-replace/lib/index.css'; +import '@univerjs/sheets-hyper-link-ui/lib/index.css'; +import '@univerjs/sheets-sort-ui/lib/index.css'; +import '@univerjs/thread-comment-ui/lib/index.css'; +import '@univerjs/sheets-zen-editor/lib/index.css'; +import '@univerjs/uniscript/lib/index.css'; diff --git a/package.json b/package.json index e46eda69633..c7796f0ac6c 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "prepare": "husky install", "pre-commit": "lint-staged", "dev": "turbo dev:demo", + "dev:libs": "pnpm --filter univer-examples dev:demo-libs", "dev:e2e": "pnpm --filter univer-examples dev:e2e", "lint:types": "turbo lint:types", "test": "turbo test -- --passWithNoTests", diff --git a/packages/engine-formula/src/index.ts b/packages/engine-formula/src/index.ts index 993638796c8..6b27e7bca20 100644 --- a/packages/engine-formula/src/index.ts +++ b/packages/engine-formula/src/index.ts @@ -155,7 +155,6 @@ export { FormulaExecutedStateType, FormulaExecuteStageType, FormulaRuntimeServic export { ISuperTableService } from './services/super-table.service'; export { SuperTableService } from './services/super-table.service'; export { deserializeRangeWithSheetWithCache } from './engine/utils/reference-cache'; -export { FFormula } from './facade/f-formula'; export { FormulaDependencyTree, type IFormulaDependencyTree } from './engine/dependency/dependency-tree'; export { type IOtherFormulaData } from './basics/common'; export { FormulaDependencyTreeType } from './engine/dependency/dependency-tree'; diff --git a/packages/facade/src/apis/__tests__/facade.spec.ts b/packages/facade/src/apis/__tests__/facade.spec.ts index eae13bd4f8a..a1aa7972b85 100644 --- a/packages/facade/src/apis/__tests__/facade.spec.ts +++ b/packages/facade/src/apis/__tests__/facade.spec.ts @@ -380,11 +380,11 @@ describe('Test FUniver', () => { expect(range.getComment()).toBeNull(); }); - it('Function registerFunction should handle async function', () => { - const functionName = 'ASYNCFUNC'; + it('Function registerFunction should handle function', () => { + const functionName = 'CUSTOMFUNC'; const functionsDisposable = univerAPI.getFormula().registerFunction(functionName, () => { return 42; - }, 'Async custom function'); + }, 'Custom function'); const descriptionService = get(IDescriptionService); const functionInfo = descriptionService.getFunctionInfo(functionName); diff --git a/packages/sheets-formula/src/facade/f-formula.ts b/packages/sheets-formula/src/facade/f-formula.ts index 45bf767b4a0..e9273c81f5b 100644 --- a/packages/sheets-formula/src/facade/f-formula.ts +++ b/packages/sheets-formula/src/facade/f-formula.ts @@ -19,7 +19,8 @@ import type { IDisposable, ILocales } from '@univerjs/core'; import type { IFunctionInfo } from '@univerjs/engine-formula'; import type { CalculationMode, IRegisterAsyncFunction, IRegisterFunction, ISingleFunctionRegisterParams, IUniverSheetsFormulaBaseConfig } from '@univerjs/sheets-formula'; import { debounce, IConfigService, ILogService, LifecycleService, LifecycleStages } from '@univerjs/core'; -import { FFormula, SetFormulaCalculationStartMutation } from '@univerjs/engine-formula'; +import { SetFormulaCalculationStartMutation } from '@univerjs/engine-formula'; +import { FFormula } from '@univerjs/engine-formula/facade'; import { IRegisterFunctionService, PLUGIN_CONFIG_KEY_BASE, RegisterFunctionService } from '@univerjs/sheets-formula'; export interface IFFormulaSheetsMixin { @@ -88,6 +89,8 @@ export interface IFFormulaSheetsMixin { * @param name - The name of the function to register. This will be used in formulas (e.g., =MYFUNC()) * @param func - The implementation of the function * @param options - Object containing locales and description + * @param options.locales - Object containing locales + * @param options.description - Object containing description * @returns A disposable object that will unregister the function when disposed * @example * ```js @@ -148,7 +151,9 @@ export interface IFFormulaSheetsMixin { * Register a custom asynchronous formula function with description. * @param name - The name of the function to register. This will be used in formulas (e.g., =ASYNCFUNC()) * @param func - The async implementation of the function - * @param description - A string describing the function's purpose and usage + * @param options - Object containing locales and description + * @param options.locales - Object containing locales + * @param options.description - Object containing description * @returns A disposable object that will unregister the function when disposed * @example * ```js @@ -287,7 +292,7 @@ export class FFormulaSheetsMixin extends FFormula implements IFFormulaSheetsMixi } FFormula.extend(FFormulaSheetsMixin); -declare module '@univerjs/engine-formula' { +declare module '@univerjs/engine-formula/facade' { // eslint-disable-next-line ts/naming-convention interface FFormula extends IFFormulaSheetsMixin {} } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bb73d190288..a3a71e2bad2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -452,6 +452,9 @@ importers: '@univerjs-infra/shared': specifier: workspace:* version: link:../common/shared + cross-env: + specifier: ^7.0.3 + version: 7.0.3 detect-port: specifier: ^1.6.1 version: 1.6.1 @@ -470,6 +473,9 @@ importers: esbuild-style-plugin: specifier: ^1.6.3 version: 1.6.3 + fast-glob: + specifier: ^3.3.3 + version: 3.3.3 fs-extra: specifier: ^11.2.0 version: 11.2.0 @@ -6390,9 +6396,10 @@ packages: typescript: optional: true - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} @@ -7401,6 +7408,10 @@ packages: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + fast-json-parse@1.0.3: resolution: {integrity: sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==} @@ -12996,7 +13007,7 @@ snapshots: '@typescript-eslint/types': 8.16.0 '@typescript-eslint/visitor-keys': 8.16.0 debug: 4.4.0 - fast-glob: 3.3.2 + fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.3 @@ -13011,7 +13022,7 @@ snapshots: '@typescript-eslint/types': 8.19.0 '@typescript-eslint/visitor-keys': 8.19.0 debug: 4.4.0 - fast-glob: 3.3.2 + fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.3 @@ -14147,11 +14158,9 @@ snapshots: optionalDependencies: typescript: 5.7.2 - cross-spawn@7.0.3: + cross-env@7.0.3: dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 + cross-spawn: 7.0.6 cross-spawn@7.0.6: dependencies: @@ -15407,7 +15416,7 @@ snapshots: execa@5.1.1: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 get-stream: 6.0.1 human-signals: 2.1.0 is-stream: 2.0.1 @@ -15465,6 +15474,14 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.7 + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-parse@1.0.3: {} fast-json-stable-stringify@2.1.0: {} @@ -15532,7 +15549,7 @@ snapshots: foreground-child@3.1.1: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 signal-exit: 4.1.0 foreground-child@3.3.0: @@ -15767,7 +15784,7 @@ snapshots: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.2 + fast-glob: 3.3.3 ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 @@ -15775,7 +15792,7 @@ snapshots: globby@14.0.2: dependencies: '@sindresorhus/merge-streams': 2.3.0 - fast-glob: 3.3.2 + fast-glob: 3.3.3 ignore: 5.3.2 path-type: 5.0.0 slash: 5.1.0