diff --git a/.github/workflows/test-wtih-vscode-build.yml b/.github/workflows/test-wtih-vscode-build.yml index 646b38a12..32d813426 100644 --- a/.github/workflows/test-wtih-vscode-build.yml +++ b/.github/workflows/test-wtih-vscode-build.yml @@ -26,7 +26,7 @@ jobs: node-version: ${{ matrix.node-version }} - run: npm install && cd vscode-web && npm install - - run: cd vscode-web && npm run clone && npm run build - - run: npm run link & npm run build + - run: cd vscode-web && npm run build + - run: npm run link && npm run build - uses: microsoft/playwright-github-action@v1 - run: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} npm run test:ci diff --git a/package-lock.json b/package-lock.json index ecea2994e..a1491a2a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "ISC", "devDependencies": { "@cloudflare/workers-types": "^4.20250109.0", - "@github1s/vscode-web": "^0.23.1", + "@github1s/vscode-web": "0.23.2", "chokidar": "^4.0.3", "clean-css": "^5.3.3", "copy-webpack-plugin": "^12.0.2", @@ -248,9 +248,9 @@ } }, "node_modules/@github1s/vscode-web": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@github1s/vscode-web/-/vscode-web-0.23.1.tgz", - "integrity": "sha512-+vq5Yh56X2cYeHVkHBS6+ewm7C63RFreL+rNLCnai9cM3Ilw4M5mq4iBBgI68WUiDJN9dc1PISiiKtgScI47Ig==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/@github1s/vscode-web/-/vscode-web-0.23.2.tgz", + "integrity": "sha512-PUBPhWevEqXLXhsP8ssmqnejA2VBuZL+i/DEI/gsZDSc+ikkf5rey7ALxRTQCnNPcPBhhtZgjABU9JO23LqWkg==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index c78b90695..553ea6b03 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "license": "ISC", "devDependencies": { "@cloudflare/workers-types": "^4.20250109.0", - "@github1s/vscode-web": "^0.23.1", + "@github1s/vscode-web": "0.23.2", "chokidar": "^4.0.3", "clean-css": "^5.3.3", "copy-webpack-plugin": "^12.0.2", diff --git a/scripts/build.js b/scripts/build.js index 25a50bebc..5d424fc84 100755 --- a/scripts/build.js +++ b/scripts/build.js @@ -3,16 +3,16 @@ import path from 'path'; import fs from 'fs-extra'; import cp from 'child_process'; -import { PROJECT_ROOT } from './utils.js'; +import { executeCommand, PROJECT_ROOT } from './utils.js'; const main = () => { for (const extension of fs.readdirSync('extensions')) { const extensionPath = path.join(PROJECT_ROOT, 'extensions', extension); if (fs.existsSync(path.join(extensionPath, 'package.json'))) { - cp.spawnSync('npm', ['run', 'compile'], { cwd: extensionPath, stdio: 'inherit' }); + executeCommand('npm', ['run', 'compile'], extensionPath); } } - cp.spawnSync('npx', ['webpack', '--mode=production'], { cwd: PROJECT_ROOT, stdio: 'inherit' }); + executeCommand('npx', ['webpack', '--mode=production'], PROJECT_ROOT); }; main(); diff --git a/scripts/link.js b/scripts/link.js index 73683af34..44b762b4f 100644 --- a/scripts/link.js +++ b/scripts/link.js @@ -2,12 +2,12 @@ import path from 'path'; import cp from 'child_process'; -import { PROJECT_ROOT } from './utils.js'; +import { executeCommand, PROJECT_ROOT } from './utils.js'; const main = () => { const distPath = path.join(PROJECT_ROOT, 'vscode-web/dist'); - cp.spawnSync('npm', ['link'], { cwd: distPath, stdio: 'inherit' }); - cp.spawnSync('npm', ['link', '@github1s/vscode-web'], { cwd: PROJECT_ROOT, stdio: 'inherit' }); + executeCommand('npm', ['link'], distPath); + executeCommand('npm', ['link', '@github1s/vscode-web'], PROJECT_ROOT); }; main(); diff --git a/scripts/postinstall.js b/scripts/postinstall.js index 1e0df51f1..b807f61c6 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -3,13 +3,13 @@ import path from 'path'; import fs from 'fs-extra'; import cp from 'child_process'; -import { PROJECT_ROOT } from './utils.js'; +import { executeCommand, PROJECT_ROOT } from './utils.js'; const main = () => { const extensions = fs.readdirSync(path.join(PROJECT_ROOT, 'extensions')); for (const extension of extensions) { const extensionPath = path.join(PROJECT_ROOT, 'extensions', extension); - cp.spawnSync('npm', ['install', '--no-save'], { cwd: extensionPath, stdio: 'inherit' }); + executeCommand('npm', ['install', '--no-save'], extensionPath); } }; diff --git a/scripts/utils.js b/scripts/utils.js index d42dd03dc..6eca04bd4 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -1,3 +1,11 @@ import path from 'path'; +import cp from 'child_process'; export const PROJECT_ROOT = path.join(import.meta.dirname, '..'); + +export const executeCommand = (command, args, cwd) => { + const result = cp.spawnSync(command, args, { stdio: 'inherit', cwd }); + if (result.error) { + throw result.error; + } +}; diff --git a/src/global.d.ts b/src/global.d.ts index 365b0f49d..db3ffabed 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -10,6 +10,7 @@ declare const DEV_VSCODE: boolean; declare const GITHUB_ORIGIN: string; declare const GITLAB_ORIGIN: string; declare const GITHUB1S_EXTENSIONS: string; +declare const AVAILABLE_LANGUAGES: string[]; /* eslint-disable no-var */ declare var dynamicImport: (url: string) => Promise; diff --git a/src/index.ts b/src/index.ts index a4e05e302..89d39c8fb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -58,13 +58,16 @@ globalThis._VSCODE_WEB = { if (!DEV_VSCODE) { const linkElement = document.createElement('link'); - linkElement.rel = 'stylesheet'; - linkElement.href = resolveVscodeUrl('vs/workbench/workbench.web.main.css'); + linkElement.setAttribute('rel', 'stylesheet'); + linkElement.setAttribute('href', resolveVscodeUrl('vs/workbench/workbench.web.main.css')); document.head.appendChild(linkElement); + const languageId = document.cookie.match(/(^| )vscode.nls.locale=([^;]+)/)?.[2] || ''; + const nlsUrl = AVAILABLE_LANGUAGES.includes(languageId) + ? resolveVscodeUrl(`../nls/${languageId}/nls.messages.js`) + : resolveVscodeUrl('nls.messages.js'); const scriptElement = document.createElement('script'); - scriptElement.type = 'text/javascript'; - scriptElement.src = resolveVscodeUrl('nls.messages.js'); + scriptElement.setAttribute('src', nlsUrl); document.body.appendChild(scriptElement); } diff --git a/vscode-web/index.html b/vscode-web/index.html index 98e0a5656..92c93eec4 100644 --- a/vscode-web/index.html +++ b/vscode-web/index.html @@ -3,23 +3,23 @@ - VSCode Web - - + VSCode Web + + - - + + diff --git a/vscode-web/package-lock.json b/vscode-web/package-lock.json index 6fb409cf5..2dce5d342 100644 --- a/vscode-web/package-lock.json +++ b/vscode-web/package-lock.json @@ -1,12 +1,12 @@ { "name": "@github1s/vscode-web", - "version": "0.23.1", + "version": "0.23.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@github1s/vscode-web", - "version": "0.23.1", + "version": "0.23.2", "license": "MIT", "devDependencies": { "chokidar": "^4.0.3", diff --git a/vscode-web/package.json b/vscode-web/package.json index 7a88f63ca..e409aba94 100644 --- a/vscode-web/package.json +++ b/vscode-web/package.json @@ -1,6 +1,6 @@ { "name": "@github1s/vscode-web", - "version": "0.23.1", + "version": "0.23.2", "description": "VS Code web for GitHub1s", "author": "github1s", "license": "MIT", @@ -12,6 +12,7 @@ "build": "run-s clone patch build:*", "build:vscode": "node scripts/build/vscode.js", "build:package": "node scripts/build/package.js", + "build:nls": "node scripts/build/nls.js", "watch": "run-s clone patch && run-p watch:*", "watch:source": "node scripts/watch/source.js", "watch:vscode": "node scripts/watch/vscode.js", diff --git a/vscode-web/scripts/build/nls.js b/vscode-web/scripts/build/nls.js new file mode 100644 index 000000000..80f6f4210 --- /dev/null +++ b/vscode-web/scripts/build/nls.js @@ -0,0 +1,66 @@ +#!/usr/bin/env node + +import path from 'path'; +import fs from 'fs-extra'; +import { PROJECT_ROOT } from '../utils.js'; + +const deepMerge = (target, source) => { + for (const key of Object.keys(source)) { + if (typeof source[key] === 'object' && target[key]) { + deepMerge(target[key], source[key]); + } else { + target[key] = source[key]; + } + } + return target; +}; + +const main = () => { + const oputBuildPath = path.join(PROJECT_ROOT, 'lib/vscode/out-build'); + const nlsKeys = JSON.parse(fs.readFileSync(path.join(oputBuildPath, 'nls.keys.json'))); + const nlsMessages = JSON.parse(fs.readFileSync(path.join(oputBuildPath, 'nls.messages.json'))); + + const languageContentsMap = {}; + const i18nPath = path.join(PROJECT_ROOT, 'lib/vscode-loc/i18n'); + + for (const languageDir of fs.readdirSync(i18nPath)) { + const languagePath = path.join(i18nPath, languageDir); + const packageJson = JSON.parse(fs.readFileSync(path.join(languagePath, 'package.json'))); + + for (const localization of packageJson.contributes.localizations) { + if (!languageContentsMap[localization.languageId]) { + languageContentsMap[localization.languageId] = {}; + } + + const contents = languageContentsMap[localization.languageId]; + for (const translation of localization.translations) { + const translationPath = path.join(languagePath, translation.path); + deepMerge(contents, JSON.parse(fs.readFileSync(translationPath)).contents); + } + } + } + + for (const languageId of Object.keys(languageContentsMap)) { + const langMessages = []; + const contents = languageContentsMap[languageId]; + + for (const [file, keys] of nlsKeys) { + for (const key of keys) { + langMessages.push(contents[file][key] || nlsMessages[langMessages.length]); + } + } + if (langMessages.length !== nlsMessages.length) { + throw new Error(`Invalid nls messages for ${languageId}`); + } + + const nslDirPath = path.join(PROJECT_ROOT, `dist/nls/${languageId}`); + fs.ensureDirSync(nslDirPath); + fs.writeFileSync( + path.join(nslDirPath, 'nls.messages.js'), + `globalThis._VSCODE_NLS_MESSAGES=${JSON.stringify(langMessages)};` + + `globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(languageId)};`, + ); + } +}; + +main(); diff --git a/vscode-web/scripts/build/vscode.js b/vscode-web/scripts/build/vscode.js index f31205d60..587e61592 100755 --- a/vscode-web/scripts/build/vscode.js +++ b/vscode-web/scripts/build/vscode.js @@ -1,12 +1,11 @@ #!/usr/bin/env node import path from 'path'; -import cp from 'child_process'; -import { PROJECT_ROOT } from '../utils.js'; +import { executeCommand, PROJECT_ROOT } from '../utils.js'; const main = () => { const cwd = path.join(PROJECT_ROOT, 'lib/vscode'); - cp.spawnSync('npm', ['run', 'gulp', 'vscode-web-min'], { stdio: 'inherit', cwd }); + executeCommand('npm', ['run', 'gulp', 'vscode-web-min'], cwd); }; main(); diff --git a/vscode-web/scripts/clone.js b/vscode-web/scripts/clone.js index 068a8a0cc..a05f50169 100644 --- a/vscode-web/scripts/clone.js +++ b/vscode-web/scripts/clone.js @@ -4,7 +4,7 @@ import path from 'path'; import cp from 'child_process'; import fs from 'fs-extra'; import crypto from 'crypto'; -import { PROJECT_ROOT, getAllFiles } from './utils.js'; +import { executeCommand, PROJECT_ROOT, getAllFiles } from './utils.js'; const fixSourceFiles = () => { const getDiffFiles = (base) => { @@ -52,13 +52,15 @@ const main = () => { const url = 'https://github.com/microsoft/vscode.git'; const ref = cp.execSync(`cat ${PROJECT_ROOT}/.VERSION`).toString(); - const commands = ['git', ['clone', '--depth', '1', '-b', ref, url, 'lib/vscode']]; - cp.spawnSync(...commands, { stdio: 'inherit', cwd: PROJECT_ROOT }); + executeCommand('git', ['clone', '--depth', '1', '-b', ref, url, 'lib/vscode'], PROJECT_ROOT); - const vscodePath = path.join(PROJECT_ROOT, 'lib/vscode'); - cp.spawnSync('npm', ['install'], { stdio: 'inherit', cwd: vscodePath }); + const locUrl = 'https://github.com/microsoft/vscode-loc.git'; + executeCommand('git', ['clone', '--depth', '1', locUrl, 'lib/vscode-loc'], PROJECT_ROOT); fixSourceFiles(); + + const vscodePath = path.join(PROJECT_ROOT, 'lib/vscode'); + executeCommand('npm', ['install'], vscodePath); }; main(); diff --git a/vscode-web/scripts/utils.js b/vscode-web/scripts/utils.js index 3cc9b67ae..12d275c8a 100644 --- a/vscode-web/scripts/utils.js +++ b/vscode-web/scripts/utils.js @@ -1,8 +1,16 @@ import path from 'path'; import fs from 'fs-extra'; +import cp from 'child_process'; export const PROJECT_ROOT = path.join(import.meta.dirname, '..'); +export const executeCommand = (command, args, cwd) => { + const result = cp.spawnSync(command, args, { stdio: 'inherit', cwd }); + if (result.error) { + throw result.error; + } +}; + export const getAllFiles = (directory) => { return fs.readdirSync(directory).flatMap((name) => { const filePath = path.join(directory, name); diff --git a/vscode-web/scripts/watch/extensions.js b/vscode-web/scripts/watch/extensions.js index 552649a5b..267676248 100644 --- a/vscode-web/scripts/watch/extensions.js +++ b/vscode-web/scripts/watch/extensions.js @@ -1,12 +1,11 @@ #!/usr/bin/env node import path from 'path'; -import cp from 'child_process'; -import { PROJECT_ROOT } from '../utils.js'; +import { executeCommand, PROJECT_ROOT } from '../utils.js'; const main = () => { const cwd = path.join(PROJECT_ROOT, 'lib/vscode'); - cp.spawnSync('npm', ['run', 'watch-web'], { stdio: 'inherit', cwd }); + executeCommand('npm', ['run', 'watch-web'], cwd); }; main(); diff --git a/vscode-web/scripts/watch/vscode.js b/vscode-web/scripts/watch/vscode.js index 83b6ffa9e..8cc676b2e 100644 --- a/vscode-web/scripts/watch/vscode.js +++ b/vscode-web/scripts/watch/vscode.js @@ -1,12 +1,11 @@ #!/usr/bin/env node import path from 'path'; -import cp from 'child_process'; -import { PROJECT_ROOT } from '../utils.js'; +import { executeCommand, PROJECT_ROOT } from '../utils.js'; const main = () => { const cwd = path.join(PROJECT_ROOT, 'lib/vscode'); - cp.spawnSync('npm', ['run', 'watch'], { stdio: 'inherit', cwd }); + executeCommand('npm', ['run', 'watch'], cwd); }; main(); diff --git a/webpack.config.js b/webpack.config.js index 51ae3e8c7..01a7315dc 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -21,6 +21,7 @@ const copyPluginPatterns = [ { from: path.join(vscodeWebPath, 'vscode'), to: `${staticDir}/vscode`, ...skipMinified }, { from: path.join(vscodeWebPath, 'extensions'), to: `${staticDir}/extensions`, ...skipMinified }, { from: path.join(vscodeWebPath, 'dependencies'), to: `${staticDir}/dependencies`, ...skipMinified }, + { from: path.join(vscodeWebPath, 'nls'), to: `${staticDir}/nls`, ...skipMinified }, ]; const devVscodeStatic = [ @@ -47,6 +48,7 @@ export default (env, argv) => { const devVscode = !!process.env.DEV_VSCODE; const minifyCSS = (code) => (devMode ? code : new CleanCSS().minify(code).styles); const minifyJS = (code) => (devMode ? code : UglifyJS.minify(code).code); + const availableLanguages = devVscode ? [] : fs.readdirSync(path.join(vscodeWebPath, 'nls')); return { mode: env.mode || 'production', @@ -84,6 +86,7 @@ export default (env, argv) => { GITHUB_ORIGIN: JSON.stringify(process.env.GITHUB_DOMAIN || 'https://github.com'), GITLAB_ORIGIN: JSON.stringify(process.env.GITLAB_DOMAIN || 'https://gitlab.com'), GITHUB1S_EXTENSIONS: JSON.stringify(packUtils.getBuiltinExtensions(devVscode)), + AVAILABLE_LANGUAGES: JSON.stringify(availableLanguages), }), ], performance: false,