From 127e5801a1bd5fcce3e02fd85cbddbab5ede67aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Tue, 30 Apr 2024 15:14:18 +0200 Subject: [PATCH] fix: optimize localization replace localization keys by indexes --- rollup/rollup.config.ts | 47 ++++++++++++++++++++++++++++++--- rollup/rollup.language-packs.ts | 22 +++++++-------- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/rollup/rollup.config.ts b/rollup/rollup.config.ts index 791a4835..633418b8 100644 --- a/rollup/rollup.config.ts +++ b/rollup/rollup.config.ts @@ -158,6 +158,8 @@ function isCallPure (file: string, functionName: string, node: recast.types.name return PURE_OR_TO_REMOVE_FUNCTIONS.has(functionName) } +const moduleNlsKeys: Record = {} + function transformVSCodeCode (id: string, code: string) { // HACK: assign typescript decorator result to a decorated class field so rollup doesn't remove them // before: @@ -206,7 +208,43 @@ function transformVSCodeCode (id: string, code: string) { const node = path.node const name = node.callee.type === 'MemberExpression' || node.callee.type === 'Identifier' ? getMemberExpressionPath(node.callee) : null - if (node.callee.type === 'MemberExpression') { + if (name != null && (name.endsWith('localizeWithPath') || name.endsWith('localize2WithPath'))) { + const translationPath = nodePath.relative(id.startsWith(OVERRIDE_PATH) ? OVERRIDE_PATH : VSCODE_SRC_DIR, id).slice(0, -3) + let localizationKey: string + if (path.node.arguments[1]?.type === 'StringLiteral') { + localizationKey = path.node.arguments[1].value + } else if (path.node.arguments[1]?.type === 'ObjectExpression') { + const properties = path.node.arguments[1].properties + const keyProperty = properties.find((prop): prop is recast.types.namedTypes.ObjectProperty => prop.type === 'ObjectProperty' && prop.key.type === 'Identifier' && prop.key.name === 'key') + if (keyProperty == null) { + throw new Error('No key property') + } + if (keyProperty.value.type !== 'StringLiteral') { + throw new Error('Key property is not literal') + } + localizationKey = keyProperty.value.value + } else if (path.node.arguments[1]?.type === 'TemplateLiteral' && path.node.arguments[1].expressions.length === 0 && path.node.arguments[1].quasis.length === 1) { + localizationKey = path.node.arguments[1].quasis[0]!.value.raw + } else { + throw new Error('Unable to extract translation key') + } + let nlsKeys = moduleNlsKeys[translationPath] + if (nlsKeys == null) { + nlsKeys = [] + moduleNlsKeys[translationPath] = nlsKeys + } + + let index = nlsKeys.indexOf(localizationKey) + if (index < 0) { + index = nlsKeys.length + nlsKeys.push(localizationKey) + } + path.replace(recast.types.builders.callExpression( + path.node.callee, + [path.node.arguments[0]!, recast.types.builders.numericLiteral(index), ...path.node.arguments.slice(2)] + )) + transformed = true + } else if (node.callee.type === 'MemberExpression') { if (node.callee.property.type === 'Identifier') { const names: string[] = [node.callee.property.name] if (name != null) { @@ -422,10 +460,10 @@ export default (args: Record): rollup.RollupOptions[] => { return undefined }, async load (id) { - if (!id.startsWith(VSCODE_SRC_DIR)) { + if (!id.startsWith(VSCODE_SRC_DIR) && !id.startsWith(OVERRIDE_PATH)) { return undefined } - if (!id.endsWith('.js')) { + if (!id.endsWith('.js') && !id.endsWith('.ts')) { return undefined } @@ -434,6 +472,9 @@ export default (args: Record): rollup.RollupOptions[] => { }, transform (code) { return code.replaceAll("'./keyboardLayouts/layout.contribution.' + platform", "'./keyboardLayouts/layout.contribution.' + platform + '.js'") + }, + async writeBundle () { + await fs.promises.writeFile(nodePath.resolve(DIST_DIR, 'nls.metadata.json'), JSON.stringify(moduleNlsKeys, null, 2)) } }, { diff --git a/rollup/rollup.language-packs.ts b/rollup/rollup.language-packs.ts index d347114f..8a25afe0 100644 --- a/rollup/rollup.language-packs.ts +++ b/rollup/rollup.language-packs.ts @@ -3,7 +3,6 @@ import * as rollup from 'rollup' import { IExtensionManifest } from 'vs/platform/extensions/common/extensions' import { dataToEsm } from '@rollup/pluginutils' import { PackageJson } from 'type-fest' -import glob from 'fast-glob' import * as fs from 'fs' import * as path from 'path' import { fileURLToPath } from 'url' @@ -22,12 +21,7 @@ const locExtensions = fs.readdirSync(LOC_PATH, { withFileTypes: true }) .filter(f => f.isDirectory() && fs.existsSync(path.resolve(LOC_PATH, f.name, 'package.json'))) .map(f => f.name) -const vscodeModules = (await glob('**/vscode/src/**/*.js', { - cwd: DIST_DIR, - onlyFiles: true -})).map(fileName => /vscode\/src\/(.*).js$/.exec(fileName)![1]!) - -const usedModules = new Set([...vscodeModules, 'vs/workbench/browser/web.main']) +const nlsMetadata: Record = JSON.parse((await fs.promises.readFile(path.resolve(DIST_DIR, 'nls.metadata.json'))).toString()) export default rollup.defineConfig([ ...locExtensions.map(name => ({ @@ -92,12 +86,18 @@ ${Object.entries(translationAssets).map(([id, assetRef]) => ` '${id}': new URL( transform (code, id) { if (!id.endsWith('.json')) return null - const parsed = JSON.parse(code).contents - // remove keys that we don't use - const filtered = Object.fromEntries(Object.entries(parsed).filter(([key]) => usedModules.has(key))) + const parsed: Record> = JSON.parse(code).contents + + const encoded = Object.fromEntries(Object.entries(nlsMetadata).map(([moduleId, keys]) => { + const values = parsed[moduleId] ?? {} + return [ + moduleId, + keys.map(key => values[key]) + ] + })) return { - code: dataToEsm(filtered, { + code: dataToEsm(encoded, { preferConst: true }), map: { mappings: '' }