├── .gitignore ├── index.js ├── setup.js ├── generate-themes.js ├── webpack.config.js ├── nls-replace.js ├── package.json ├── common.js ├── update-types.js ├── README.md ├── generate-extraLibs.js ├── theme-extra ├── ace.json ├── forge-light.json └── forge-dark.json ├── demo.html ├── node-red-types ├── util.d.ts └── func.d.ts ├── LICENSE └── generate-monaco-esm-i18n.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | output 3 | temp 4 | .cache 5 | .vscode -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // import * as monaco from 'monaco-editor-esm-i18n'; 2 | // window.monaco = monaco; 3 | // window.MonacoEnvironment = window.MonacoEnvironment || {}; 4 | // if(!window.MonacoEnvironment.Locale) { 5 | // window.MonacoEnvironment.Locale = window.MonacoLocale 6 | // } 7 | 8 | import * as monaco from 'monaco-editor-esm-i18n'; 9 | const parent = typeof globalThis === "object" ? globalThis : typeof window === "object" ? window : typeof self === "object" ? self : global; 10 | parent.monaco = monaco; 11 | monaco.parent = parent; 12 | parent.MonacoEnvironment = parent.MonacoEnvironment || {}; 13 | if(!parent.MonacoEnvironment.Locale) { 14 | parent.MonacoEnvironment.Locale = parent.MonacoLocale 15 | } -------------------------------------------------------------------------------- /setup.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | exports.projectDir = __dirname; 4 | exports.srcDir = path.join(exports.projectDir); 5 | exports.targetDir = path.join(exports.projectDir, "output"); 6 | exports.tempDir = path.join(exports.projectDir, "temp"); 7 | exports.npmDir = path.join(exports.srcDir); 8 | 9 | exports.nodeModulesDir= path.join(exports.npmDir, "node_modules"); 10 | exports.monacoDir = path.join(exports.nodeModulesDir, "monaco-editor"); 11 | 12 | exports.monacoModDir = path.join(exports.nodeModulesDir, "monaco-editor-esm-i18n"); 13 | exports.monacoModEsmDir= path.join(exports.monacoModDir, "esm"); 14 | exports.monacoThemesDir= path.join(exports.npmDir, "monaco-themes/themes"); 15 | 16 | exports.gitDir = path.join(exports.tempDir, "git"); 17 | exports.vsCodeLocDir = path.join(exports.gitDir, "vscode-loc"); 18 | exports.vsCodeLocI18nDir = path.join(exports.vsCodeLocDir, "i18n"); 19 | exports.generatedSourceLocaleDir = path.join(exports.targetDir, "monaco/dist/locale"); 20 | 21 | exports.MINIFY_DTS = false; 22 | exports.NODE_VERSION_TO_INCLUDE = "v20.14.8"; 23 | exports.NODE_LIB_SOURCE = path.join(exports.projectDir, 'node_modules/@types/node'); 24 | exports.NODE_LIB_DESTINATION = path.join(exports.projectDir, 'output/types/node'); 25 | 26 | exports.NODE_RED_LIB_SOURCE = path.join(exports.projectDir, 'node-red-types'); 27 | exports.NODE_RED_LIB_DESTINATION = path.join(exports.projectDir, 'output/types/node-red'); -------------------------------------------------------------------------------- /generate-themes.js: -------------------------------------------------------------------------------- 1 | /* 2 | Generates monaco compatible themes from tm themes and renames the file to that of 3 | */ 4 | const path = require('path'); 5 | const parseTmTheme = require('monaco-themes').parseTmTheme; 6 | const fs = require('fs'); 7 | 8 | const exclude = ["themelist.json"]; 9 | const SOURCE1 = path.join(__dirname, 'node_modules/monaco-themes/themes'); 10 | const SOURCE2 = path.join(__dirname, 'theme-extra'); 11 | const DESTINATION = path.join(__dirname, 'output/monaco/dist/theme'); 12 | 13 | 14 | 15 | (function () { 16 | try { 17 | fs.statSync(DESTINATION); 18 | } catch (err) { 19 | fs.mkdirSync(DESTINATION, { recursive: true }); 20 | } 21 | importThemes(SOURCE1, DESTINATION); 22 | importThemes(SOURCE2, DESTINATION); 23 | })(); 24 | 25 | 26 | function importThemes(source, destination) { 27 | function readFile(name, dir) { 28 | const srcPath = path.join(dir, name); 29 | return fs.readFileSync(srcPath).toString(); 30 | } 31 | 32 | function getNewFileName(src) { 33 | let newName = src.toLowerCase(); 34 | if (newName.endsWith(".tmtheme")) { 35 | newName = newName.replace(".tmtheme", ".json") 36 | } 37 | newName = newName.split(" ").join("-"); 38 | newName = newName.split("[").join(""); 39 | newName = newName.split("]").join(""); 40 | newName = newName.split("(").join(""); 41 | newName = newName.split(")").join(""); 42 | return newName 43 | } 44 | 45 | const themeFiles = fs.readdirSync(source); 46 | while (themeFiles.length > 0) { 47 | const srcFileName = themeFiles.shift(); 48 | let skip = exclude.includes(srcFileName); 49 | if (skip) continue; 50 | 51 | let tmThemeString = readFile(srcFileName, source); 52 | var themeData = tmThemeString; 53 | let dstFileName = srcFileName; 54 | if (dstFileName.toLowerCase().endsWith(".json")) { 55 | dstFileName = getNewFileName(srcFileName); 56 | } else if (dstFileName.toLowerCase().endsWith(".tmtheme")) { 57 | dstFileName = getNewFileName(srcFileName); 58 | let monacoTheme = parseTmTheme(tmThemeString); 59 | themeData = JSON.stringify(monacoTheme, null, 2); 60 | } 61 | console.log(`Adding ${srcFileName} as ${dstFileName}`) 62 | fs.writeFileSync(path.join(destination, dstFileName), themeData); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const { monacoModDir } = require("./setup"); 3 | const monacoModPath = path.resolve(__dirname, "node_modules", monacoModDir); 4 | const nls = require.resolve("./nls-replace.js"); 5 | const LimitChunkCountPlugin = require("webpack/lib/optimize/LimitChunkCountPlugin"); 6 | const NormalModuleWebpackReplacementPlugin = require("webpack/lib/NormalModuleReplacementPlugin"); 7 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 8 | const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); 9 | 10 | module.exports = (env, argv) => { 11 | const devMode = ["development", "dev"].includes(argv?.mode || "production"); 12 | env = env || {}; 13 | env.production = env.production === true || env.production === "true" || !devMode 14 | // env.production = false; // force dev mode build 15 | if (env.production) { 16 | console.log("Running webpack in production mode"); 17 | } else { 18 | console.log("Running webpack in development mode"); 19 | } 20 | return { 21 | mode: env.production ? "production" : "development", 22 | devtool: env.production ? undefined : "inline-source-map", 23 | entry: { editor: "./index.js" }, 24 | output: { 25 | path: path.resolve(__dirname, "output", "monaco", "dist"), 26 | filename: 'editor.js' 27 | }, 28 | stats: { 29 | assets: true, 30 | children: true, 31 | chunks: false, 32 | errors: true, 33 | errorDetails: true, 34 | modules: false, 35 | timings: true, 36 | colors: true 37 | }, 38 | target: ['web', 'es6'], 39 | module: { 40 | rules: [{ 41 | test: /\.css$/, 42 | use: ["style-loader", "css-loader"], 43 | }, { 44 | test: /\.ttf$/, 45 | type: 'asset/resource' 46 | } 47 | ] 48 | }, 49 | plugins: [ 50 | new NormalModuleWebpackReplacementPlugin(/\/(vscode\-)?nls\.js/, function (resource) { 51 | resource.request = nls; 52 | resource.resource = nls; 53 | }), 54 | new MonacoWebpackPlugin({ globalAPI: true, monacoEditorPath: monacoModPath }), 55 | new LimitChunkCountPlugin({ 56 | maxChunks: 1, 57 | }), 58 | new CopyWebpackPlugin({ 59 | patterns: [ 60 | { from: 'node_modules/monaco-themes/LICENSE', to: "theme" }, 61 | { from: 'node_modules/monaco-editor-esm-i18n/ThirdPartyNotices.txt', to: "ThirdPartyNotices.txt" }, 62 | { from: 'node_modules/monaco-editor-esm-i18n/LICENSE', to: "LICENSE", toType: "file" } 63 | ] 64 | }), 65 | ], 66 | 67 | optimization: { 68 | minimize: env.production ? true : false, 69 | splitChunks: { 70 | minSize: 9999999999999999, 71 | } 72 | } 73 | }; 74 | }; 75 | -------------------------------------------------------------------------------- /nls-replace.js: -------------------------------------------------------------------------------- 1 | // Modified to support localization. 2 | /*--------------------------------------------------------------------------------------------- 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * Licensed under the MIT License. See License.txt in the project root for license information. 5 | *--------------------------------------------------------------------------------------------*/ 6 | const globalScope = typeof globalThis === "object" ? globalThis : typeof window === "object" ? window : typeof self === "object" ? self : global; 7 | let isPseudo = (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0); 8 | 9 | function _format(message, args) { 10 | let result; 11 | if (args.length === 0) { 12 | result = message; 13 | } 14 | else { 15 | result = message.replace(/\{(\d+)\}/g, function (match, rest) { 16 | const index = rest[0]; 17 | return typeof args[index] !== 'undefined' ? args[index] : match; 18 | }); 19 | } 20 | if (isPseudo) { 21 | // FF3B and FF3D is the Unicode zenkaku representation for [ and ] 22 | result = '\uFF3B' + result.replace(/[aouei]/g, '$&$&') + '\uFF3D'; 23 | } 24 | return result; 25 | } 26 | 27 | /** 28 | * @skipMangle 29 | */ 30 | export function localize(path, data, defaultMessage, ...args) { 31 | const key = typeof data=== "object" ? data.key : data; 32 | // data = ((globalScope.MonacoEnvironment||{}).Locale||{}).data||{}; 33 | // data = ((globalScope.MonacoLocale || {}) || {}).data || {}; 34 | // let message = (data[path]||{})[key]; 35 | const localeData = ((globalScope.MonacoLocale || {}) || {}).data || {}; 36 | let message = (localeData[path] || {})[key]; 37 | if (!message) { 38 | message = defaultMessage; 39 | } 40 | args = []; 41 | for (let _i = 3; _i < arguments.length; _i++) { 42 | args[_i - 3] = arguments[_i]; 43 | } 44 | return _format(message, args); 45 | } 46 | /** 47 | * @skipMangle 48 | */ 49 | export function localize2(path, data, defaultMessage, ...args) { 50 | const key = typeof data=== "object" ? data.key : data; 51 | // data = ((globalScope.MonacoEnvironment||{}).Locale||{}).data||{}; 52 | // data = ((globalScope.MonacoLocale || {}) || {}).data || {}; 53 | // let message = (data[path]||{})[key]; 54 | const localeData = ((globalScope.MonacoLocale || {}) || {}).data || {}; 55 | let message = (localeData[path] || {})[key]; 56 | if (!message) { 57 | message = defaultMessage; 58 | } 59 | args = []; 60 | for (let _i = 3; _i < arguments.length; _i++) { 61 | args[_i - 3] = arguments[_i]; 62 | } 63 | const original = _format(message, args); 64 | return { 65 | value: original, 66 | original 67 | }; 68 | } 69 | 70 | export function loadMessageBundle(file) { 71 | return localize; 72 | } 73 | 74 | export function config(opt) { 75 | return loadMessageBundle; 76 | } 77 | 78 | /** 79 | * @skipMangle 80 | */ 81 | export function getConfiguredDefaultLocale() { 82 | return (self.MonacoLocale || {}).language; 83 | } 84 | /** 85 | * @skipMangle 86 | */ 87 | export function getNLSLanguage() { 88 | return (self.MonacoLocale || {}).language; 89 | } 90 | export function getNLSMessages() { 91 | return ((self.MonacoLocale || {}) || {}).data || {}; 92 | } 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nr-monaco-build", 3 | "version": "0.52.2", 4 | "description": "A wrapper to generate an es6 monaco editor with i18n, specifically developed for node-red", 5 | "private": true, 6 | "main": "index.js", 7 | "scripts": { 8 | "build": "npm run update-types && npm run generate && npm run generate-themes && npm run pack", 9 | "build-dev": "npm run update-types && npm run generate && npm run generate-themes && npm run pack-dev", 10 | "copy": "npm run copyCheck1 && npm run copyCheck1 && npm run copyPrep1 && npm run copyPrep2 && npm run copyPrep3 && npm run copyAct1 && npm run copyAct2", 11 | "all": "npm run clean && npm run build && npm run copy", 12 | "all-dev": "npm run clean && npm run build-dev && npm run copy", 13 | "clean": "rm -r -f output && rm -r -f temp", 14 | "generate": "node generate-monaco-esm-i18n.js", 15 | "generate-themes": "node generate-themes.js", 16 | "update-types": "node update-types.js", 17 | "pack": "npx webpack", 18 | "pack-dev": "npx webpack --mode development", 19 | "demo": "npx http-server -p 8080 -c-1 -o demo.html", 20 | "generate-extraLibs": "node generate-extraLibs.js", 21 | "copyCheck1": "test -d output/monaco/dist", 22 | "copyCheck2": "test -d ../node-red/packages/node_modules/@node-red/editor-client/src", 23 | "copyPrep1": "rm -rf ../node-red/packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/*", 24 | "copyPrep2": "rm -rf ../node-red/packages/node_modules/@node-red/editor-client/src/types/node/*", 25 | "copyPrep3": "rm -rf ../node-red/packages/node_modules/@node-red/editor-client/src/types/node-red/*", 26 | "copyAct1": "cp -r output/monaco/dist ../node-red/packages/node_modules/@node-red/editor-client/src/vendor/monaco/", 27 | "copyAct2": "cp -r output/types ../node-red/packages/node_modules/@node-red/editor-client/src" 28 | }, 29 | "keywords": [ 30 | "monaco", 31 | "node-red" 32 | ], 33 | "author": "steve-mcl", 34 | "license": "Apache-2.0", 35 | "devDependencies": { 36 | "@types/node": "^20.14.8", 37 | "copy-webpack-plugin": "^13.0.1", 38 | "css-loader": "^7.1.2", 39 | "degit": "^2.8.4", 40 | "dts-minify": "^0.3.3", 41 | "glob": "^10.2.6", 42 | "mkdirp": "^3.0.1", 43 | "monaco-editor": "0.52.2", 44 | "monaco-editor-webpack-plugin": "^7.1.0", 45 | "monaco-themes": "^0.4.7", 46 | "ncp": "^2.0.0", 47 | "read-package-json": "^7.0.1", 48 | "recursive-readdir": "^2.2.3", 49 | "replace-in-file": "^6.3.2", 50 | "rimraf": "^5.0.5", 51 | "style-loader": "^3.3.3", 52 | "typedoc": "^0.28.11", 53 | "typescript": "^5.9.2", 54 | "url-loader": "^4.1.1", 55 | "webpack": "^5.101.3", 56 | "webpack-cli": "^5.1.4" 57 | }, 58 | "browserslist": [ 59 | "> 1%", 60 | "last 2 versions" 61 | ], 62 | "staticFiles": { 63 | "staticPath": [ 64 | { 65 | "outDirPattern": "**/dist", 66 | "staticPath": "theme", 67 | "staticOutDir": "theme" 68 | }, 69 | { 70 | "outDirPattern": "**/dist-legacy", 71 | "staticPath": "theme", 72 | "staticOutDir": "theme" 73 | } 74 | ] 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /common.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const _semver = require("semver"); 4 | 5 | function findClosestSemverMatch(semverString, semverStringArray) { 6 | if (!semverStringArray.length) { 7 | return null 8 | } 9 | let semversArray = semverStringArray.map(e => semver(e)); 10 | const semverB = semver(semverString); 11 | const semversSameMajor = semversArray.filter(e => e.major == semverB.major); 12 | if(semversSameMajor && semversSameMajor.length) { 13 | semversArray = semversSameMajor; 14 | } 15 | semversArray.sort(function(a, b){ 16 | if(a.major < b.major) return -1; 17 | else if(a.major > b.major) return 1; 18 | else { 19 | if(a.minor < b.minor) return -1; 20 | else if(a.minor > b.minor) return 1; 21 | else { 22 | if(a.revision < b.revision) return -1; 23 | else if(a.revision > b.revision) return 1; 24 | } 25 | } 26 | }); 27 | 28 | let below, above, best; 29 | for (let index = 0; index < semversArray.length; index++) { 30 | const element = semversArray[index]; 31 | if(element.major == semverB.major && element.minor == semverB.minor && element.patch == semverB.patch) { 32 | best = element; 33 | break; 34 | } 35 | if(element.major <= semverB.major && element.minor <= semverB.minor && element.patch < semverB.patch) { 36 | below = element; 37 | } 38 | if(element.major >= semverB.major && element.minor >= semverB.minor && element.patch > semverB.patch) { 39 | above = element; 40 | break; 41 | } 42 | } 43 | if(!best) { 44 | best = above ? above : below; 45 | } 46 | if(!best) { 47 | return null; 48 | } 49 | 50 | return best.toString(); 51 | } 52 | 53 | function semver(/** @type {string} */ ver) { 54 | let verClean = (ver+""); 55 | verClean = verClean.replace(">","").replace("<","").replace("^","").replace("~","").replace("=",""); 56 | const parts = verClean.split('.'); 57 | if(parts.length === 1) parts.push("0"); 58 | if(parts.length === 2) parts.push("0"); 59 | if(parts.length > 3) { 60 | parts[2] = parts.slice(2).join("."); 61 | } 62 | 63 | const p1 = ((parts[0] + "") || "0").trim(); 64 | const p2 = ((parts[1] + "") || "0").trim(); 65 | const p3 = ((parts[2] + "") || "0").trim(); 66 | const version = _semver.parse(`${p1}.${p2}.${p3}`); 67 | 68 | return version; 69 | } 70 | 71 | function mkDirSafe(dst) { 72 | try { 73 | fs.statSync(dst); 74 | } catch (err) { 75 | fs.mkdirSync(dst, { recursive: true }); 76 | } 77 | } 78 | 79 | 80 | //this is deliberately long hand since rmdirSync did not support .recursive {option} until v12.10.0 81 | function deleteFileOrDir(_path){ 82 | if (fs.existsSync(_path)) { 83 | if (fs.lstatSync(_path).isDirectory()) { 84 | var files = fs.readdirSync(_path); 85 | if (!files.length) return fs.rmdirSync(_path); 86 | for (var file in files) { 87 | var currentPath = path.join(_path, files[file]); 88 | if (!fs.existsSync(currentPath)) continue; 89 | if (fs.lstatSync(currentPath).isFile()) { 90 | fs.unlinkSync(currentPath); 91 | continue; 92 | } 93 | if (fs.lstatSync(currentPath).isDirectory() && !fs.readdirSync(currentPath).length) { 94 | fs.rmdirSync(currentPath); 95 | } else { 96 | deleteFileOrDir(currentPath, _path); 97 | } 98 | } 99 | deleteFileOrDir(_path); 100 | } else { 101 | fs.unlinkSync(_path); 102 | } 103 | } 104 | } 105 | 106 | exports.findClosestSemverMatch = findClosestSemverMatch; 107 | exports.semver = semver; 108 | exports.deleteFileOrDir = deleteFileOrDir; 109 | exports.mkDirSafe = mkDirSafe; -------------------------------------------------------------------------------- /update-types.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | const path = require('path'); 5 | const fs = require('fs'); 6 | const { execSync } = require('child_process'); 7 | const note = `\n/* NOTE: Do not edit directly! This file is generated using \`npm run update-types\` in https://github.com/node-red/nr-monaco-build */\n\n`; 8 | const excludeLibs = ["base.d.ts", "constants.d.ts", "index.d.ts", "inspector.d.ts", "punycode.d.ts", "globals.global.d.ts", "repl.d.ts"]; 9 | 10 | const { findClosestSemverMatch, deleteFileOrDir, mkDirSafe } = require("./common"); 11 | 12 | const { 13 | MINIFY_DTS, 14 | NODE_LIB_SOURCE, 15 | NODE_LIB_DESTINATION, 16 | NODE_RED_LIB_SOURCE, 17 | NODE_RED_LIB_DESTINATION, 18 | NODE_VERSION_TO_INCLUDE 19 | } = require("./setup"); 20 | 21 | let ts, minifier; 22 | if (MINIFY_DTS) { 23 | const { createMinifier } = require("dts-minify"); 24 | ts = require("typescript"); 25 | minifier = createMinifier(ts);// setup (provide a TS Compiler API object) 26 | } 27 | 28 | (function () { 29 | let nodeVer = NODE_VERSION_TO_INCLUDE || process.version 30 | if (nodeVer[0] === "v") { nodeVer = nodeVer.substring(1) } 31 | 32 | //get available nodejs types from npm 33 | const cmd1 = `npm view @types/node versions --json` 34 | const r1 = execSync(cmd1) 35 | 36 | //determine closes version 37 | const versions = JSON.parse(r1); 38 | const closestVersion = findClosestSemverMatch(nodeVer, versions); 39 | 40 | //install @types/node@closestVersion 41 | const cmd2 = `npm i -s @types/node@${closestVersion} --save-dev`; 42 | execSync(cmd2) 43 | //import the libs from NODE_LIB_SOURCE to NODE_LIB_DESTINATION 44 | deleteFileOrDir(NODE_LIB_DESTINATION); 45 | deleteFileOrDir(NODE_RED_LIB_DESTINATION); 46 | copyFiles(NODE_LIB_SOURCE, NODE_LIB_DESTINATION); 47 | copyFiles(NODE_RED_LIB_SOURCE, NODE_RED_LIB_DESTINATION); 48 | })(); 49 | 50 | 51 | function copyFiles(src, dst) { 52 | function readLibFile(name, dir) { 53 | const srcPath = path.join(dir, name); 54 | return fs.readFileSync(srcPath).toString(); 55 | } 56 | function getDirectories(_path) { 57 | return fs.readdirSync(_path).filter(function (file) { 58 | return fs.statSync(path.join(_path, file)).isDirectory(); 59 | }); 60 | } 61 | const nodeDtsFiles = fs.readdirSync(src).filter((f) => f.includes('.ts')); 62 | if (nodeDtsFiles && nodeDtsFiles.length) { 63 | var files = nodeDtsFiles.filter(e => excludeLibs.includes(e) == false);//remove excluded files 64 | if (files.length) { 65 | console.log(`Copy '${files.length}' file(s) from '${src}' to '${dst}'...`) 66 | mkDirSafe(dst); 67 | while (files.length > 0) { 68 | const name = files.shift(); 69 | console.log(`Copying '${name}'`) 70 | const output = note + readLibFile(name, src).replace(/\r\n/g, '\n'); 71 | if (minifier) { 72 | const minifiedText = minifier.minify(output, { 73 | keepJsDocs: true, // false by default 74 | }); 75 | fs.writeFileSync(path.join(dst, name), minifiedText); 76 | } else { 77 | fs.writeFileSync(path.join(dst, name), output); 78 | } 79 | 80 | } 81 | } 82 | } 83 | const dirs = getDirectories(src); 84 | if (dirs && dirs.length) { 85 | for (let index = 0; index < dirs.length; index++) { 86 | const p = dirs[index]; 87 | const srcDir = path.join(src, p); 88 | const dstDir = path.join(dst, p); 89 | copyFiles(srcDir, dstDir); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ***IMPORTANT NOTE: This project is specifically for building monaco for node-red and will likely not be of much use to any other projects*** 4 | 5 |
6 | 7 | ## About 8 | This project makes an ESM bundle of monaco-editor with 50 themes and localization support. It was built specifically for use in node-red. 9 | 10 | ## Credits 11 | * Huge credit to [primefaces-monaco](https://github.com/blutorange/primefaces-monaco). Without their work, I would never have gotten i18n working. 12 | * All credits to https://www.npmjs.com/package/monaco-themes for the themes 13 | 14 | 15 | ## Notes 16 | * A bug / issue I had to handle is: when changing mode, html worker would attempt to get links from document, but occasionally `document` would be `null` and an exception would be thrown. This has been handled by adding `if(!document) return []` at the top of `function findDocumentLinks(...)` in file `htmlLinks.js` 17 | 18 | ## Instructions 19 | 20 | ### Clone repo 21 | ```bash 22 | git clone https://github.com/node-red/nr-monaco-build 23 | cd nr-monaco-build 24 | ``` 25 | 26 | ### Prepare 27 | 28 | #### Step 1 29 | 30 | Prepare the build: 31 | 32 | 1. Check & update `package.json` for latest version of `monaco-editor` (check [here](https://www.npmjs.com/package/monaco-editor)) and other dev dependencies 33 | 2. Update the `package.json` `version` field to match the version of `monaco-editor` you are using. 34 | 3. Check + update `setup.js` in particular the node version set in `env.NODE_VERSION_TO_INCLUDE` This should match a version found in [@types/node](https://www.npmjs.com/package/@types/node?activeTab=versions) on NPM e.g. `v20.14.8` 35 | 36 | #### Step 2 37 | 38 | Check + update node-red (function node/server-side) type defs 39 | * `./node-red-types/func.d.ts` 40 | * `./node-red-types/util.d.ts` 41 | 42 | ### Step 3 43 | 44 | Install dependencies, clean and build 45 | 46 | ```bash 47 | npm install --include=dev 48 | npm run clean 49 | npm run build 50 | ``` 51 | 52 | This will bundle the monaco editor with localization support and themes: 53 | 54 | ```bash 55 | cd output/monaco/dist/ 56 | ``` 57 | 58 | ### Check it works 59 | 60 | Check editor works in browser... 61 | 62 | ```bash 63 | npm run demo 64 | ``` 65 | 66 | Now go to 67 | 68 | ``` 69 | http://localhost:8080/demo.html 70 | ``` 71 | 72 | and you should see monaco editor with a theme set and foreign menus (try opening the context menu with a right click) 73 | 74 | ### Add to node-red src 75 | 76 | #### Automatically 77 | 78 | If your Node-RED source is relative to this repo, you can run the following helper script: 79 | 80 | ```bash 81 | npm run copy 82 | ``` 83 | 84 | #### Manually 85 | 86 | When your Node-RED source is not relative to this repo, you can copy the files manually: 87 | 88 | ```bash 89 | # Set the path to your node-red source e.g. 90 | export nr_src=~/repos/github/node-red-org/node-red 91 | # clean up 92 | rm -rf $nr_src/packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/* 93 | rm -rf $nr_src/packages/node_modules/@node-red/editor-client/src/types/node/* 94 | rm -rf $nr_src/packages/node_modules/@node-red/editor-client/src/types/node-red/* 95 | 96 | # copy 97 | cp -r output/monaco/dist \ 98 | $nr_src/packages/node_modules/@node-red/editor-client/src/vendor/monaco/ 99 | cp -r output/types \ 100 | $nr_src/packages/node_modules/@node-red/editor-client/src/ 101 | 102 | ``` 103 | 104 | ### Additional helper scripts 105 | 106 | #### Build monaco in development mode 107 | 108 | ```bash 109 | npm run build-dev 110 | ``` 111 | 112 | 113 | #### All in one (production mode) 114 | 115 | This will run `npm run clean`, `npm run build`, `npm run copy` in sequence 116 | 117 | ```bash 118 | npm run all 119 | ``` 120 | 121 | #### All in one (development mode) 122 | 123 | This will run `npm run clean`, `npm run build-dev`, `npm run copy` in sequence 124 | 125 | ```bash 126 | npm run all-dev 127 | ``` 128 | -------------------------------------------------------------------------------- /generate-extraLibs.js: -------------------------------------------------------------------------------- 1 | /* 2 | Generates a single file containing monacoExtraLibs with an index lookup and the types as strings in a lookup 3 | This version IS NOt USED - but could be utilised at a later date. 4 | Worth keeping this code for "just in case" 5 | */ 6 | const path = require('path'); 7 | const fs = require('fs'); 8 | const child_process = require('child_process'); 9 | const note = `\n/* NOTE: Do not edit directly! This file is generated using \`npm run generate-extraLibs\` */\n`; 10 | const enableDefault = ["console.d.ts", "globals.d.ts", "buffer.d.ts", "red-util.d.ts", "red-func.d.ts"]; 11 | const excludeLibs = ["base.d.ts","constants.d.ts","index.d.ts","inspector.d.ts","punycode.d.ts", "globals.global.d.ts", "repl.d.ts"]; 12 | const NODE_LIB_SOURCE = path.join(__dirname, 'node_modules/@types/node'); 13 | const NODE_RED_LIB_SOURCE = path.join(__dirname, 'node-red-types'); 14 | const LIB_DESTINATION = path.join(__dirname, 'output/types/extraLibs'); 15 | 16 | (function () { 17 | try { 18 | fs.statSync(LIB_DESTINATION); 19 | } catch (err) { 20 | fs.mkdirSync(LIB_DESTINATION, { recursive: true }); 21 | } 22 | importLibs(); 23 | 24 | })(); 25 | 26 | 27 | function importLibs() { 28 | function readLibFile(name, dir) { 29 | const srcPath = path.join(dir, name); 30 | return fs.readFileSync(srcPath).toString(); 31 | } 32 | 33 | let strLib = `${note} 34 | /** default export */ 35 | export const monacoExtraLibs = {} 36 | `; 37 | 38 | let strLibResult = `${note} 39 | /** lib files */ 40 | monacoExtraLibs.typeMap = {} 41 | `; 42 | 43 | let strIndexResult = `${note} 44 | /** lib index */ 45 | monacoExtraLibs.typeSet = {} 46 | `; 47 | const nodeDtsFiles = fs.readdirSync(NODE_LIB_SOURCE).filter((f) => f.includes('.ts')); 48 | while (nodeDtsFiles.length > 0) { 49 | const name = nodeDtsFiles.shift(); 50 | let skip = excludeLibs.includes(name); 51 | if(skip) continue; 52 | console.log(`Adding ${name}`) 53 | const output = readLibFile(name, NODE_LIB_SOURCE).replace(/\r\n/g, '\n'); 54 | strLibResult += `monacoExtraLibs.typeMap['node/${name}'] = "${escapeText(output)}";\n`; 55 | let en = enableDefault.includes(name); 56 | strIndexResult += `monacoExtraLibs.typeSet['node/${name}'] = ${en};\n`; 57 | } 58 | const noderedDtsFiles = fs.readdirSync(NODE_RED_LIB_SOURCE).filter((f) => f.includes('.ts')); 59 | while (noderedDtsFiles.length > 0) { 60 | const name = noderedDtsFiles.shift(); 61 | let skip = excludeLibs.includes(name); 62 | if(skip) continue; 63 | console.log(`Adding ${name}`) 64 | const output = readLibFile(name, NODE_RED_LIB_SOURCE).replace(/\r\n/g, '\n'); 65 | strLibResult += `monacoExtraLibs.typeMap['red/${name}'] = "${escapeText(output)}";\n`; 66 | strIndexResult += `monacoExtraLibs.typeSet['red/${name}'] = true;\n`; 67 | } 68 | 69 | fs.writeFileSync(path.join(LIB_DESTINATION, 'extraLibs.js'), strLib + strIndexResult + strLibResult ); 70 | } 71 | 72 | /** 73 | * Escape text such that it can be used in a javascript string enclosed by double quotes (") 74 | */ 75 | function escapeText(text) { 76 | // See http://www.javascriptkit.com/jsref/escapesequence.shtml 77 | const _backspace = '\b'.charCodeAt(0); 78 | const _formFeed = '\f'.charCodeAt(0); 79 | const _newLine = '\n'.charCodeAt(0); 80 | const _nullChar = 0; 81 | const _carriageReturn = '\r'.charCodeAt(0); 82 | const _tab = '\t'.charCodeAt(0); 83 | const _verticalTab = '\v'.charCodeAt(0); 84 | const _backslash = '\\'.charCodeAt(0); 85 | const _doubleQuote = '"'.charCodeAt(0); 86 | 87 | const len = text.length; 88 | let startPos = 0; 89 | let chrCode; 90 | let replaceWith = null; 91 | let resultPieces = []; 92 | 93 | for (let i = 0; i < len; i++) { 94 | chrCode = text.charCodeAt(i); 95 | switch (chrCode) { 96 | case _backspace: 97 | replaceWith = '\\b'; 98 | break; 99 | case _formFeed: 100 | replaceWith = '\\f'; 101 | break; 102 | case _newLine: 103 | replaceWith = '\\n'; 104 | break; 105 | case _nullChar: 106 | replaceWith = '\\0'; 107 | break; 108 | case _carriageReturn: 109 | replaceWith = '\\r'; 110 | break; 111 | case _tab: 112 | replaceWith = '\\t'; 113 | break; 114 | case _verticalTab: 115 | replaceWith = '\\v'; 116 | break; 117 | case _backslash: 118 | replaceWith = '\\\\'; 119 | break; 120 | case _doubleQuote: 121 | replaceWith = '\\"'; 122 | break; 123 | } 124 | if (replaceWith !== null) { 125 | resultPieces.push(text.substring(startPos, i)); 126 | resultPieces.push(replaceWith); 127 | startPos = i + 1; 128 | replaceWith = null; 129 | } 130 | } 131 | resultPieces.push(text.substring(startPos, len)); 132 | return resultPieces.join(''); 133 | } 134 | 135 | function stripSourceMaps(str) { 136 | return str.replace(/\/\/# sourceMappingURL[^\n]+/gm, ''); 137 | } 138 | -------------------------------------------------------------------------------- /theme-extra/ace.json: -------------------------------------------------------------------------------- 1 | { 2 | "base": "vs", 3 | "inherit": true, 4 | "rules": [ 5 | { 6 | "token": "", 7 | "foreground": "5c6773" 8 | }, 9 | { 10 | "token": "invalid", 11 | "foreground": "ff3333" 12 | }, 13 | { 14 | "token": "emphasis", 15 | "fontStyle": "italic" 16 | }, 17 | { 18 | "token": "strong", 19 | "fontStyle": "bold" 20 | }, 21 | { 22 | "token": "variable", 23 | "foreground": "5c6773" 24 | }, 25 | { 26 | "token": "variable.predefined", 27 | "foreground": "5c6773" 28 | }, 29 | { 30 | "token": "constant", 31 | "foreground": "f08c36" 32 | }, 33 | { 34 | "token": "comment", 35 | "foreground": "abb0b6", 36 | "fontStyle": "italic" 37 | }, 38 | { 39 | "token": "number", 40 | "foreground": "f08c36" 41 | }, 42 | { 43 | "token": "number.hex", 44 | "foreground": "f08c36" 45 | }, 46 | { 47 | "token": "regexp", 48 | "foreground": "4dbf99" 49 | }, 50 | { 51 | "token": "annotation", 52 | "foreground": "41a6d9" 53 | }, 54 | { 55 | "token": "type", 56 | "foreground": "41a6d9" 57 | }, 58 | { 59 | "token": "delimiter", 60 | "foreground": "5c6773" 61 | }, 62 | { 63 | "token": "delimiter.html", 64 | "foreground": "5c6773" 65 | }, 66 | { 67 | "token": "delimiter.xml", 68 | "foreground": "5c6773" 69 | }, 70 | { 71 | "token": "tag", 72 | "foreground": "e7c547" 73 | }, 74 | { 75 | "token": "tag.id.jade", 76 | "foreground": "e7c547" 77 | }, 78 | { 79 | "token": "tag.class.jade", 80 | "foreground": "e7c547" 81 | }, 82 | { 83 | "token": "meta.scss", 84 | "foreground": "e7c547" 85 | }, 86 | { 87 | "token": "metatag", 88 | "foreground": "e7c547" 89 | }, 90 | { 91 | "token": "metatag.content.html", 92 | "foreground": "86b300" 93 | }, 94 | { 95 | "token": "metatag.html", 96 | "foreground": "e7c547" 97 | }, 98 | { 99 | "token": "metatag.xml", 100 | "foreground": "e7c547" 101 | }, 102 | { 103 | "token": "metatag.php", 104 | "fontStyle": "bold" 105 | }, 106 | { 107 | "token": "key", 108 | "foreground": "41a6d9" 109 | }, 110 | { 111 | "token": "string.key.json", 112 | "foreground": "41a6d9" 113 | }, 114 | { 115 | "token": "string.value.json", 116 | "foreground": "86b300" 117 | }, 118 | { 119 | "token": "attribute.name", 120 | "foreground": "f08c36" 121 | }, 122 | { 123 | "token": "attribute.value", 124 | "foreground": "0451A5" 125 | }, 126 | { 127 | "token": "attribute.value.number", 128 | "foreground": "abb0b6" 129 | }, 130 | { 131 | "token": "attribute.value.unit", 132 | "foreground": "86b300" 133 | }, 134 | { 135 | "token": "attribute.value.html", 136 | "foreground": "86b300" 137 | }, 138 | { 139 | "token": "attribute.value.xml", 140 | "foreground": "86b300" 141 | }, 142 | { 143 | "token": "string", 144 | "foreground": "86b300" 145 | }, 146 | { 147 | "token": "string.html", 148 | "foreground": "86b300" 149 | }, 150 | { 151 | "token": "string.sql", 152 | "foreground": "86b300" 153 | }, 154 | { 155 | "token": "string.yaml", 156 | "foreground": "86b300" 157 | }, 158 | { 159 | "token": "keyword", 160 | "foreground": "f2590c" 161 | }, 162 | { 163 | "token": "keyword.json", 164 | "foreground": "f2590c" 165 | }, 166 | { 167 | "token": "keyword.flow", 168 | "foreground": "f2590c" 169 | }, 170 | { 171 | "token": "keyword.flow.scss", 172 | "foreground": "f2590c" 173 | }, 174 | { 175 | "token": "operator.scss", 176 | "foreground": "666666" 177 | }, 178 | { 179 | "token": "operator.sql", 180 | "foreground": "778899" 181 | }, 182 | { 183 | "token": "operator.swift", 184 | "foreground": "666666" 185 | }, 186 | { 187 | "token": "predefined.sql", 188 | "foreground": "FF00FF" 189 | } 190 | ], 191 | "colors": { 192 | "editor.background": "#fafafa", 193 | "editor.foreground": "#5c6773", 194 | "editorIndentGuide.background": "#ecebec", 195 | "editorIndentGuide.activeBackground": "#e0e0e0" 196 | } 197 | } -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

Demonstrating monaco editor esm with i18n

15 |

Things to check...

16 | 21 | 22 |
MONACO
23 | 24 | 25 | 26 | 27 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /theme-extra/forge-light.json: -------------------------------------------------------------------------------- 1 | { 2 | "base": "vs", 3 | "inherit": true, 4 | "rules": [ 5 | { 6 | "background": "f2f3fb", 7 | "token": "" 8 | }, 9 | { 10 | "foreground": "8e908c", 11 | "token": "comment" 12 | }, 13 | { 14 | "foreground": "31959A", 15 | "token": "keyword.operator.class" 16 | }, 17 | { 18 | "foreground": "31959A", 19 | "token": "constant.other" 20 | }, 21 | { 22 | "foreground": "c82829", 23 | "token": "variable" 24 | }, 25 | { 26 | "foreground": "c82829", 27 | "token": "support.other.variable" 28 | }, 29 | { 30 | "foreground": "c82829", 31 | "token": "string.other.link" 32 | }, 33 | { 34 | "foreground": "c82829", 35 | "token": "string.regexp" 36 | }, 37 | { 38 | "foreground": "c82829", 39 | "token": "entity.name.tag" 40 | }, 41 | { 42 | "foreground": "c82829", 43 | "token": "entity.other.attribute-name" 44 | }, 45 | { 46 | "foreground": "c82829", 47 | "token": "meta.tag" 48 | }, 49 | { 50 | "foreground": "c82829", 51 | "token": "declaration.tag" 52 | }, 53 | { 54 | "foreground": "c82829", 55 | "token": "markup.deleted.git_gutter" 56 | }, 57 | { 58 | "foreground": "f5871f", 59 | "token": "constant.numeric" 60 | }, 61 | { 62 | "foreground": "f5871f", 63 | "token": "constant.language" 64 | }, 65 | { 66 | "foreground": "f5871f", 67 | "token": "support.constant" 68 | }, 69 | { 70 | "foreground": "f5871f", 71 | "token": "constant.character" 72 | }, 73 | { 74 | "foreground": "f5871f", 75 | "token": "variable.parameter" 76 | }, 77 | { 78 | "foreground": "f5871f", 79 | "token": "punctuation.section.embedded" 80 | }, 81 | { 82 | "foreground": "f5871f", 83 | "token": "keyword.other.unit" 84 | }, 85 | { 86 | "foreground": "c99e00", 87 | "token": "entity.name.class" 88 | }, 89 | { 90 | "foreground": "c99e00", 91 | "token": "entity.name.type.class" 92 | }, 93 | { 94 | "foreground": "c99e00", 95 | "token": "support.type" 96 | }, 97 | { 98 | "foreground": "c99e00", 99 | "token": "support.class" 100 | }, 101 | { 102 | "foreground": "ED4E4E", 103 | "token": "string" 104 | }, 105 | { 106 | "foreground": "ED4E4E", 107 | "token": "constant.other.symbol" 108 | }, 109 | { 110 | "foreground": "ED4E4E", 111 | "token": "entity.other.inherited-class" 112 | }, 113 | { 114 | "foreground": "ED4E4E", 115 | "token": "markup.heading" 116 | }, 117 | { 118 | "foreground": "718c00", 119 | "token": "markup.inserted.git_gutter" 120 | }, 121 | { 122 | "foreground": "31959A", 123 | "token": "keyword.operator" 124 | }, 125 | { 126 | "foreground": "31959A", 127 | "token": "constant.other.color" 128 | }, 129 | { 130 | "foreground": "4271ae", 131 | "token": "entity.name.function" 132 | }, 133 | { 134 | "foreground": "4271ae", 135 | "token": "meta.function-call" 136 | }, 137 | { 138 | "foreground": "4271ae", 139 | "token": "support.function" 140 | }, 141 | { 142 | "foreground": "4271ae", 143 | "token": "keyword.other.special-method" 144 | }, 145 | { 146 | "foreground": "4271ae", 147 | "token": "meta.block-level" 148 | }, 149 | { 150 | "foreground": "4271ae", 151 | "token": "markup.changed.git_gutter" 152 | }, 153 | { 154 | "foreground": "31959A", 155 | "token": "keyword" 156 | }, 157 | { 158 | "foreground": "8959a8", 159 | "token": "storage" 160 | }, 161 | { 162 | "foreground": "8959a8", 163 | "token": "storage.type" 164 | }, 165 | { 166 | "foreground": "f12229", 167 | "token": "invalid" 168 | }, 169 | { 170 | "foreground": "ffffff", 171 | "background": "4271ae", 172 | "token": "meta.separator" 173 | }, 174 | { 175 | "foreground": "f12229", 176 | "background": "8959a8", 177 | "token": "invalid.deprecated" 178 | }, 179 | { 180 | "background": "718c00", 181 | "token": "markup.inserted.diff" 182 | }, 183 | { 184 | "background": "718c00", 185 | "token": "meta.diff.header.to-file" 186 | }, 187 | { 188 | "background": "c82829", 189 | "token": "markup.deleted.diff" 190 | }, 191 | { 192 | "background": "c82829", 193 | "token": "meta.diff.header.from-file" 194 | }, 195 | { 196 | "foreground": "ffffff", 197 | "background": "4271ae", 198 | "token": "meta.diff.header.from-file" 199 | }, 200 | { 201 | "foreground": "ffffff", 202 | "background": "4271ae", 203 | "token": "meta.diff.header.to-file" 204 | }, 205 | { 206 | "foreground": "31959A", 207 | "fontStyle": "italic", 208 | "token": "meta.diff.range" 209 | } 210 | ], 211 | "colors": { 212 | "editor.foreground": "#1f2937", 213 | "editor.background": "#ffffff", 214 | "editorCursor.foreground": "#4E4F5D", 215 | "editorLineNumber.foreground": "#1f2937", 216 | "dropdown.background": "#F7F8FC", 217 | "editor.selectionBackground": "#65718370", 218 | "editor.inactiveSelectionBackground": "#65718330", 219 | "editorWidget.background":"#F7F8FC", 220 | "list.hoverForeground": "#F7F8FC", 221 | "list.hoverBackground": "#657183", 222 | "list.focusForeground": "#F7F8FC", 223 | "list.focusBackground": "#657183", 224 | "list.activeSelectionForeground": "#F7F8FC", 225 | "list.activeSelectionBackground": "#657183" 226 | } 227 | } -------------------------------------------------------------------------------- /theme-extra/forge-dark.json: -------------------------------------------------------------------------------- 1 | { 2 | "base": "vs-dark", 3 | "inherit": true, 4 | "rules": [ 5 | { 6 | "background": "1f2937", 7 | "token": "" 8 | }, 9 | { 10 | "foreground": "6272a4", 11 | "token": "comment" 12 | }, 13 | { 14 | "foreground": "f1fa8c", 15 | "token": "string" 16 | }, 17 | { 18 | "foreground": "bd93f9", 19 | "token": "constant.numeric" 20 | }, 21 | { 22 | "foreground": "bd93f9", 23 | "token": "constant.language" 24 | }, 25 | { 26 | "foreground": "bd93f9", 27 | "token": "constant.character" 28 | }, 29 | { 30 | "foreground": "bd93f9", 31 | "token": "constant.other" 32 | }, 33 | { 34 | "foreground": "ffb86c", 35 | "token": "variable.other.readwrite.instance" 36 | }, 37 | { 38 | "foreground": "ff79c6", 39 | "token": "constant.character.escaped" 40 | }, 41 | { 42 | "foreground": "ff79c6", 43 | "token": "constant.character.escape" 44 | }, 45 | { 46 | "foreground": "ff79c6", 47 | "token": "string source" 48 | }, 49 | { 50 | "foreground": "ff79c6", 51 | "token": "string source.ruby" 52 | }, 53 | { 54 | "foreground": "ff79c6", 55 | "token": "keyword" 56 | }, 57 | { 58 | "foreground": "ff79c6", 59 | "token": "storage" 60 | }, 61 | { 62 | "foreground": "8be9fd", 63 | "fontStyle": "italic", 64 | "token": "storage.type" 65 | }, 66 | { 67 | "foreground": "50fa7b", 68 | "fontStyle": "underline", 69 | "token": "entity.name.class" 70 | }, 71 | { 72 | "foreground": "50fa7b", 73 | "fontStyle": "italic underline", 74 | "token": "entity.other.inherited-class" 75 | }, 76 | { 77 | "foreground": "50fa7b", 78 | "token": "entity.name.function" 79 | }, 80 | { 81 | "foreground": "ffb86c", 82 | "fontStyle": "italic", 83 | "token": "variable.parameter" 84 | }, 85 | { 86 | "foreground": "ff79c6", 87 | "token": "entity.name.tag" 88 | }, 89 | { 90 | "foreground": "50fa7b", 91 | "token": "entity.other.attribute-name" 92 | }, 93 | { 94 | "foreground": "8be9fd", 95 | "token": "support.function" 96 | }, 97 | { 98 | "foreground": "6be5fd", 99 | "token": "support.constant" 100 | }, 101 | { 102 | "foreground": "66d9ef", 103 | "fontStyle": " italic", 104 | "token": "support.type" 105 | }, 106 | { 107 | "foreground": "66d9ef", 108 | "fontStyle": " italic", 109 | "token": "support.class" 110 | }, 111 | { 112 | "foreground": "f8f8f0", 113 | "background": "ff79c6", 114 | "token": "invalid" 115 | }, 116 | { 117 | "foreground": "f8f8f0", 118 | "background": "bd93f9", 119 | "token": "invalid.deprecated" 120 | }, 121 | { 122 | "foreground": "cfcfc2", 123 | "token": "meta.structure.dictionary.json string.quoted.double.json" 124 | }, 125 | { 126 | "foreground": "6272a4", 127 | "token": "meta.diff" 128 | }, 129 | { 130 | "foreground": "6272a4", 131 | "token": "meta.diff.header" 132 | }, 133 | { 134 | "foreground": "ff79c6", 135 | "token": "markup.deleted" 136 | }, 137 | { 138 | "foreground": "50fa7b", 139 | "token": "markup.inserted" 140 | }, 141 | { 142 | "foreground": "e6db74", 143 | "token": "markup.changed" 144 | }, 145 | { 146 | "foreground": "bd93f9", 147 | "token": "constant.numeric.line-number.find-in-files - match" 148 | }, 149 | { 150 | "foreground": "e6db74", 151 | "token": "entity.name.filename" 152 | }, 153 | { 154 | "foreground": "f83333", 155 | "token": "message.error" 156 | }, 157 | { 158 | "foreground": "eeeeee", 159 | "token": "punctuation.definition.string.begin.json - meta.structure.dictionary.value.json" 160 | }, 161 | { 162 | "foreground": "eeeeee", 163 | "token": "punctuation.definition.string.end.json - meta.structure.dictionary.value.json" 164 | }, 165 | { 166 | "foreground": "8be9fd", 167 | "token": "meta.structure.dictionary.json string.quoted.double.json" 168 | }, 169 | { 170 | "foreground": "f1fa8c", 171 | "token": "meta.structure.dictionary.value.json string.quoted.double.json" 172 | }, 173 | { 174 | "foreground": "50fa7b", 175 | "token": "meta meta meta meta meta meta meta.structure.dictionary.value string" 176 | }, 177 | { 178 | "foreground": "ffb86c", 179 | "token": "meta meta meta meta meta meta.structure.dictionary.value string" 180 | }, 181 | { 182 | "foreground": "ff79c6", 183 | "token": "meta meta meta meta meta.structure.dictionary.value string" 184 | }, 185 | { 186 | "foreground": "bd93f9", 187 | "token": "meta meta meta meta.structure.dictionary.value string" 188 | }, 189 | { 190 | "foreground": "50fa7b", 191 | "token": "meta meta meta.structure.dictionary.value string" 192 | }, 193 | { 194 | "foreground": "ffb86c", 195 | "token": "meta meta.structure.dictionary.value string" 196 | } 197 | ], 198 | "colors": { 199 | "editor.foreground": "#f8f8f2", 200 | "editor.background": "#1f2937", 201 | "editorCursor.foreground": "#f8f8f0", 202 | "dropdown.background": "#44475a", 203 | "editor.selectionBackground": "#44475a70", 204 | "editor.inactiveSelectionBackground": "#44475a30", 205 | "editorWidget.background": "#44475a", 206 | "list.hoverForeground": "#44475a", 207 | "list.hoverBackground": "#F7F8FC", 208 | "list.focusForeground": "#44475a", 209 | "list.focusBackground": "#F7F8FC", 210 | "list.activeSelectionForeground": "#44475a", 211 | "list.activeSelectionBackground": "#F7F8FC" 212 | } 213 | } -------------------------------------------------------------------------------- /node-red-types/util.d.ts: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | How to generate... 4 | 1. Generate from packages\node_modules\@node-red\util\lib\util.js using `npx typescript` and a tsconfig.json of... 5 | { 6 | "files": ["packages/node_modules/@node-red/util/lib/util.js"], 7 | "compilerOptions": { 8 | "allowJs": true, 9 | "declaration": true, 10 | "emitDeclarationOnly": true, 11 | "outDir": "types", 12 | "strict": false, 13 | "moduleResolution": "node" 14 | } 15 | } 16 | 2. remove all the `export ` statements 17 | 3. Wrap the remaining code in declare namespace RED { declare namespace util { ... } } 18 | 4. check . adjust types like String --> string, Object --> object etc (where appropriate) 19 | */ 20 | 21 | declare namespace RED { 22 | /** 23 | * Utility functions for the node-red function sandbox 24 | */ 25 | namespace util { 26 | 27 | /** 28 | * Encode an object to JSON without losing information about non-JSON types 29 | * such as Buffer and Function. 30 | * 31 | * *This function is closely tied to its reverse within the editor* 32 | * 33 | * @param {Object} msg 34 | * @param {Object} opts 35 | * @return {Object} the encoded object 36 | * @memberof @node-red/util_util 37 | */ 38 | function encodeObject(msg: any, opts: any): any; 39 | /** 40 | * Converts the provided argument to a String, using type-dependent 41 | * methods. 42 | * 43 | * @param {any} o - the property to convert to a String 44 | * @return {string} the stringified version 45 | * @memberof @node-red/util_util 46 | */ 47 | function ensureString(o: any): string; 48 | /** 49 | * Converts the provided argument to a Buffer, using type-dependent 50 | * methods. 51 | * 52 | * @param {any} o - the property to convert to a Buffer 53 | * @return {string} the Buffer version 54 | * @memberof @node-red/util_util 55 | */ 56 | function ensureBuffer(o: any): string; 57 | /** 58 | * Safely clones a message object. This handles msg.req/msg.res objects that must 59 | * not be cloned. 60 | * 61 | * @param {object} msg - the message object to clone 62 | * @return {object} the cloned message 63 | * @memberof @node-red/util_util 64 | */ 65 | function cloneMessage(msg: object): object; 66 | /** 67 | * Compares two objects, handling various JavaScript types. 68 | * 69 | * @param {any} obj1 70 | * @param {any} obj2 71 | * @return {boolean} whether the two objects are the same 72 | * @memberof @node-red/util_util 73 | */ 74 | function compareObjects(obj1: any, obj2: any): boolean; 75 | /** 76 | * Generates a psuedo-unique-random id. 77 | * @return {string} a random-ish id 78 | * @memberof @node-red/util_util 79 | */ 80 | function generateId(): string; 81 | /** 82 | * Gets a property of a message object. 83 | * 84 | * Unlike {@link @node-red/util-util.getObjectProperty}, this function will strip `msg.` from the 85 | * front of the property expression if present. 86 | * 87 | * @param {object} msg - the message object 88 | * @param {string} expr - the property expression 89 | * @return {any} the message property, or undefined if it does not exist 90 | * @throws Will throw an error if the *parent* of the property does not exist 91 | * @memberof @node-red/util_util 92 | */ 93 | function getMessageProperty(msg: object, expr: string): any; 94 | /** 95 | * Sets a property of a message object. 96 | * 97 | * Unlike {@link @node-red/util-util.setObjectProperty}, this function will strip `msg.` from the 98 | * front of the property expression if present. 99 | * 100 | * @param {object} msg - the message object 101 | * @param {string} prop - the property expression 102 | * @param {any} [value] - the value to set 103 | * @param {boolean} [createMissing] - whether to create missing parent properties 104 | * @memberof @node-red/util_util 105 | */ 106 | function setMessageProperty(msg: object, prop: string, value?: any, createMissing?: boolean): boolean; 107 | /** 108 | * Gets a property of an object. 109 | * 110 | * Given the object: 111 | * 112 | * { 113 | * "pet": { 114 | * "type": "cat" 115 | * } 116 | * } 117 | * 118 | * - `pet.type` will return `"cat"`. 119 | * - `pet.name` will return `undefined` 120 | * - `car` will return `undefined` 121 | * - `car.type` will throw an Error (as `car` does not exist) 122 | * 123 | * @param {object} msg - the object 124 | * @param {string} expr - the property expression 125 | * @return {any} the object property, or undefined if it does not exist 126 | * @throws Will throw an error if the *parent* of the property does not exist 127 | * @memberof @node-red/util_util 128 | */ 129 | function getObjectProperty(msg: object, expr: string): any; 130 | /** 131 | * Sets a property of an object. 132 | * 133 | * @param {object} msg - the object 134 | * @param {string} prop - the property expression 135 | * @param {any} [value] - the value to set 136 | * @param {boolean} [createMissing] - whether to create missing parent properties 137 | * @memberof @node-red/util_util 138 | */ 139 | function setObjectProperty(msg: object, prop: string, value?: any, createMissing?: boolean): boolean; 140 | /** 141 | * Evaluates a property value according to its type. 142 | * 143 | * @param {string} value - the raw value 144 | * @param {string} type - the type of the value 145 | * @param {Node} node - the node evaluating the property 146 | * @param {Object} msg - the message object to evaluate against 147 | * @param {Function} callback - (optional) called when the property is evaluated 148 | * @return {any} The evaluated property, if no `callback` is provided 149 | * @memberof @node-red/util_util 150 | */ 151 | function evaluateNodeProperty(value: string, type: string, node: Node, msg: any, callback: Function): any; 152 | /** 153 | * Parses a property expression, such as `msg.foo.bar[3]` to validate it 154 | * and convert it to a canonical version expressed as an Array of property 155 | * names. 156 | * 157 | * For example, `a["b"].c` returns `['a','b','c']` 158 | * 159 | * @param {string} str - the property expression 160 | * @return {any[]} the normalised expression 161 | * @memberof @node-red/util_util 162 | */ 163 | function normalisePropertyExpression(str: string): any[]; 164 | /** 165 | * Normalise a node type name to camel case. 166 | * 167 | * For example: `a-random node type` will normalise to `aRandomNodeType` 168 | * 169 | * @param {string} name - the node type 170 | * @return {string} The normalised name 171 | * @memberof @node-red/util_util 172 | */ 173 | function normaliseNodeTypeName(name: string): string; 174 | /** 175 | * Prepares a JSONata expression for evaluation. 176 | * This attaches Node-RED specific functions to the expression. 177 | * 178 | * @param {string} value - the JSONata expression 179 | * @param {Node} node - the node evaluating the property 180 | * @return {any} The JSONata expression that can be evaluated 181 | * @memberof @node-red/util_util 182 | */ 183 | function prepareJSONataExpression(value: string, node: Node): any; 184 | /** 185 | * Evaluates a JSONata expression. 186 | * The expression must have been prepared with {@link @node-red/util-util.prepareJSONataExpression} 187 | * before passing to this function. 188 | * 189 | * @param {Object} expr - the prepared JSONata expression 190 | * @param {Object} msg - the message object to evaluate against 191 | * @param {Function} callback - (optional) called when the expression is evaluated 192 | * @return {any} If no callback was provided, the result of the expression 193 | * @memberof @node-red/util_util 194 | */ 195 | function evaluateJSONataExpression(expr: any, msg: any, callback: Function): any; 196 | /** 197 | * Parses a context property string, as generated by the TypedInput, to extract 198 | * the store name if present. 199 | * 200 | * For example, `#:(file)::foo` results in ` { store: "file", key: "foo" }`. 201 | * 202 | * @param {string} key - the context property string to parse 203 | * @return {any} The parsed property 204 | * @memberof @node-red/util_util 205 | */ 206 | function parseContextStore(key: string): any; 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright OpenJS Foundation and other contributors, https://openjsf.org/ 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | -------------------------------------------------------------------------------- /generate-monaco-esm-i18n.js: -------------------------------------------------------------------------------- 1 | const degit = require('degit'); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const recursive = require("recursive-readdir"); 5 | const mkdirp = require("mkdirp"); 6 | const { replaceInFileSync } = require("replace-in-file"); 7 | const ncp = require("ncp").ncp; 8 | const { rimraf } = require("rimraf"); 9 | const readJson = require('read-package-json') 10 | const { semver } = require("./common"); 11 | 12 | const { 13 | monacoDir, 14 | monacoModDir, 15 | monacoModEsmDir, 16 | gitDir, 17 | vsCodeLocDir, 18 | vsCodeLocI18nDir, 19 | generatedSourceLocaleDir, 20 | } = require("./setup"); 21 | 22 | const langDirPrefix = "vscode-language-pack-"; 23 | const vsCodeRepository = "https://github.com/Microsoft/vscode-loc.git"; 24 | const fileExistsCache = new Map(); 25 | 26 | function readPackage(packageFile) { 27 | return new Promise(function (resolve, reject) { 28 | readJson(packageFile, console.error, false, (err, data) => { 29 | if (err) { 30 | reject(err); 31 | } else { 32 | resolve(data) 33 | } 34 | }); 35 | }); 36 | } 37 | 38 | /** 39 | * The microsoft/vscode-loc contains many more i18n keys that are used by monaco editor. 40 | * Keys are grouped by source files, so we include only those keys whose source files 41 | * exists in the monaco editor repository. 42 | * @param {string} key 43 | * @return {boolean} 44 | */ 45 | function sourceFileExists(key) { 46 | if (fileExistsCache.has(key)) { 47 | return fileExistsCache.get(key); 48 | } 49 | const filePath = path.join(monacoModEsmDir, key + ".js"); 50 | const exists = fs.existsSync(filePath); 51 | fileExistsCache.set(key, exists); 52 | return exists; 53 | } 54 | 55 | /** 56 | * The call to the `localize` function only include the i18 key, but translations 57 | * in the microsoft/vscode-loc repository are first grouped by source file name, 58 | * then i18n key. So we modify the each call to `localize` so that it includes 59 | * the source file name. 60 | * 61 | * > nls.localize(key, args) 62 | * 63 | * becomes 64 | * 65 | * > nls.localize("source/file.js", key, args) 66 | * @param callback 67 | */ 68 | function injectSourcePath(monacoVersion, callback) { 69 | rimraf(monacoModDir).then(() => { 70 | ncp(monacoDir, monacoModDir, function (err) { 71 | if (err) { 72 | callback(err); 73 | return; 74 | } 75 | recursive(monacoModEsmDir, (err, files) => { 76 | if (err) { 77 | callback(err); 78 | return; 79 | } 80 | files.forEach(file => { 81 | if (file.endsWith(".js")) { 82 | const vsPath = path.relative(monacoModEsmDir, path.dirname(file)).replace(/\\/g, "/"); 83 | const transPath = vsPath + "/" + path.basename(file, ".js"); 84 | //1a. Find localize( : localize( localize2( .localize2( etc (but NOT function localize() ) 85 | //1b. Change to localize("path/to/translation/item", xxxx 86 | //2a. Find localize.apply( 87 | //2b. Change to localize.apply("path/to/translation/item", xxxxx 88 | replaceInFileSync({ 89 | files: file, 90 | from: [ 91 | /(? -1) { 143 | //insert version: 144 | //1. Find const api = createMonacoBaseAPI(); 145 | //2. Insert Object.defineProperty(api, 'version', {get: function() { return 'x.y.z' }}); 146 | // export const version = api.version; 147 | replaceInFileSync({ 148 | files: file, 149 | from: [ 150 | /(const\s+?)(\w*?)(\s+?=\s+?createMonacoBaseAPI\(\).*$)/gm 151 | ], 152 | to: [ 153 | `$1$2$3` + 154 | `\nObject.defineProperty($2, 'version', {get: function() { return '${monacoVersion}' }});` + 155 | `\nexport const version = $2.version;` 156 | ], 157 | }); 158 | } 159 | } 160 | }); 161 | callback(); 162 | }); 163 | }); 164 | }).catch(err => { 165 | callback(err); 166 | }) 167 | } 168 | 169 | /** 170 | * Reads all files from the microsoft/vscode-loc repository for the given language 171 | * and creates one object with all i18n keys. 172 | * @param lang Language, eg. `en` or `de`. 173 | * @param langPath Full path to the directory with the language files. 174 | * @param callback Called on completion with error and the created locale object. 175 | */ 176 | function createLocale(lang, langPath, callback) { 177 | const locale = {}; 178 | const allTranslations = {}; 179 | recursive(langPath, function (err, files) { 180 | if (err) { 181 | callback(err); 182 | return; 183 | } 184 | files.forEach(file => { 185 | if (file.endsWith(".i18n.json")) { 186 | const data = fs.readFileSync(file, { encoding: "UTF-8" }); 187 | let json; 188 | try { 189 | json = JSON.parse(data); 190 | } 191 | catch (e1) { 192 | try { 193 | json = eval("(" + data + ")"); 194 | } 195 | catch (e2) { 196 | const newErr = new Error("Error while parsing i18n file " + file); 197 | newErr.stack += "\n\ncaused by: " + e2.stack; 198 | callback(newErr); 199 | return; 200 | } 201 | } 202 | if (typeof json.contents !== "object") { 203 | console.warn("no translations found", file); 204 | return; 205 | } 206 | delete json.contents["package"]; 207 | for (const key of Object.keys(json.contents)) { 208 | if (key) { 209 | if (sourceFileExists(key)) { 210 | locale[key] = json.contents[key]; 211 | } 212 | allTranslations[key] = json.contents[key]; 213 | } 214 | } 215 | } 216 | }); 217 | callback(undefined, locale); 218 | }); 219 | } 220 | 221 | function createScript(lang, locale) { 222 | const sortedKeys = Object.keys(locale).sort((lhs, rhs) => { 223 | const l = lhs.toLowerCase(); 224 | const r = rhs.toLowerCase(); 225 | return l < r ? -1 : l > r ? 1 : 0; 226 | }); 227 | const sortedLocale = {}; 228 | for (const key of sortedKeys) { 229 | sortedLocale[key] = locale[key]; 230 | // sortedLocale[safeKey(key)] = locale[key]; 231 | } 232 | return `window.MonacoEnvironment = window.MonacoEnvironment || {}; 233 | window.MonacoEnvironment.Locale = window.MonacoLocale = { 234 | language: '${lang}', 235 | data: ${JSON.stringify(sortedLocale, null, 2)} 236 | };`; 237 | } 238 | 239 | async function main() { 240 | const pkg = await readPackage("package.json"); 241 | const monacoDep = pkg.devDependencies["monaco-editor"]; 242 | const monacoSemver = semver(monacoDep); 243 | const monacoVersion = monacoSemver.toString(); 244 | 245 | mkdirp.sync(gitDir); 246 | injectSourcePath(monacoVersion, err => { 247 | if (err) throw err; 248 | const emitter = degit('Microsoft/vscode-loc', { force: true }); 249 | emitter.clone(vsCodeLocDir).then(() => { 250 | fs.readdir(vsCodeLocI18nDir, (err, langDirs) => { 251 | if (err) throw err; 252 | langDirs.forEach(langDir => { 253 | if (!langDir.startsWith(langDirPrefix)) { 254 | return; 255 | } 256 | const lang = langDir.substring(langDirPrefix.length).toLowerCase(); 257 | const transPath = path.join(vsCodeLocI18nDir, langDir, "translations"); 258 | if (fs.existsSync(transPath) && fs.lstatSync(transPath).isDirectory()) { 259 | createLocale(lang, transPath, (err, locale) => { 260 | if (err) throw err; 261 | mkdirp.sync(generatedSourceLocaleDir) 262 | const mappedLang = lang; 263 | fs.writeFile(path.join(generatedSourceLocaleDir, mappedLang + ".js"), createScript(mappedLang, locale), { encoding: "UTF-8" }, err => { 264 | if (err) throw err; 265 | console.log("generated locale " + mappedLang + ".js"); 266 | }); 267 | }); 268 | } 269 | }) 270 | }); 271 | }).catch(err => { 272 | throw err; 273 | }); 274 | }); 275 | } 276 | 277 | main(); 278 | -------------------------------------------------------------------------------- /node-red-types/func.d.ts: -------------------------------------------------------------------------------- 1 | interface NodeMessage { 2 | topic?: string; 3 | payload?: any; 4 | /** `_msgid` is generated internally. It not something you typically need to set or modify. */ _msgid?: string; 5 | [other: string]: any; //permit other properties 6 | } 7 | 8 | /** @type {NodeMessage} the `msg` object */ 9 | declare var msg: NodeMessage; 10 | /** @type {string} the id of the incoming `msg` (alias of msg._msgid) */ 11 | declare const __msgid__:string; 12 | 13 | declare const util:typeof import('util') 14 | declare const promisify:typeof import('util').promisify 15 | 16 | /** 17 | * @typedef NodeStatus 18 | * @type {object} 19 | * @property {'red'|'green'|'yellow'|'blue'|'grey'|string} [fill] - The fill property can be: red, green, yellow, blue or grey. 20 | * @property {'ring'|'dot'|string} [shape] The shape property can be: ring or dot. 21 | * @property {string|boolean|number} [text] The text to display 22 | */ 23 | interface NodeStatus { 24 | /** The fill property can be: red, green, yellow, blue or grey */ 25 | fill?: 'red'|'green'|'yellow'|'blue'|'grey'|string, 26 | /** The shape property can be: ring or dot */ 27 | shape?: 'ring'|'dot'|string, 28 | /** The text to display */ 29 | text?: string|boolean|number 30 | } 31 | 32 | declare class node { 33 | /** 34 | * Send 1 or more messages asynchronously 35 | * @param {object | object[]} msg The msg object 36 | * @param {Boolean} [clone=true] Flag to indicate the `msg` should be cloned. Default = `true` 37 | * @see Node-RED documentation [writing-functions: sending messages asynchronously](https://nodered.org/docs/user-guide/writing-functions#sending-messages-asynchronously) 38 | */ 39 | static send(msg:NodeMessage|NodeMessage[], clone?:Boolean): void; 40 | /** Inform runtime this instance has completed its operation */ 41 | static done(); 42 | /** Send an error to the console and debug side bar. Include `msg` in the 2nd parameter to trigger the catch node. */ 43 | static error(err:string|Error, msg?:NodeMessage); 44 | /** Log a warn message to the console and debug sidebar */ 45 | static warn(warning:string|object); 46 | /** Log an info message to the console (not sent to sidebar)' */ 47 | static log(info:string|object); 48 | /** Sets the status icon and text underneath the node. 49 | * @param {NodeStatus} status - The status object `{fill, shape, text}` 50 | * @see Node-RED documentation [writing-functions: adding-status](https://nodered.org/docs/user-guide/writing-functions#adding-status) 51 | */ 52 | static status(status:NodeStatus); 53 | /** Sets the status text underneath the node. 54 | * @see Node-RED documentation [writing-functions: adding-status](https://nodered.org/docs/user-guide/writing-functions#adding-status) 55 | */ 56 | static status(status:string|boolean|number); 57 | /** the id of this node */ 58 | public static readonly id:string; 59 | /** the name of this node */ 60 | public static readonly name:string; 61 | /** the path identifier for this node */ 62 | public static readonly path:string; 63 | /** the number of outputs of this node */ 64 | public static readonly outputCount:number; 65 | } 66 | declare class context { 67 | /** 68 | * Get one or multiple values from context (synchronous). 69 | * @param name - Name of context variable 70 | */ 71 | static get(name: string | string[]); 72 | /** 73 | * Get one or multiple values from context (asynchronous). 74 | * @param name - Name (or array of names) to get from context 75 | * @param {function} callback - (optional) Callback function (`(err,value) => {}`) 76 | */ 77 | static get(name: string | string[], callback: Function); 78 | /** 79 | * Get one or multiple values from context (synchronous). 80 | * @param name - Name (or array of names) to get from context 81 | * @param store - Name of context store 82 | */ 83 | static get(name: string | string[], store: string); 84 | /** 85 | * Get one or multiple values from context (asynchronous). 86 | * @param name - Name (or array of names) to get from context 87 | * @param store - Name of context store 88 | * @param {function} callback - (optional) Callback function (`(err,value) => {}`) 89 | */ 90 | static get(name: string | string[], store: string, callback: Function); 91 | 92 | 93 | /** 94 | * Set one or multiple values in context (synchronous). 95 | * @param name - Name (or array of names) to set in context 96 | * @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed. 97 | */ 98 | static set(name: string | string[], value?: any | any[]); 99 | /** 100 | * Set one or multiple values in context (asynchronous). 101 | * @param name - Name (or array of names) to set in context 102 | * @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed. 103 | * @param callback - (optional) Callback function (`(err) => {}`) 104 | */ 105 | static set(name: string | string[], value?: any | any[], callback?: Function); 106 | /** 107 | * Set one or multiple values in context (synchronous). 108 | * @param name - Name (or array of names) to set in context 109 | * @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed. 110 | * @param store - (optional) Name of context store 111 | */ 112 | static set(name: string | string[], value?: any | any[], store?: string); 113 | /** 114 | * Set one or multiple values in context (asynchronous). 115 | * @param name - Name (or array of names) to set in context 116 | * @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed. 117 | * @param store - (optional) Name of context store 118 | * @param callback - (optional) Callback function (`(err) => {}`) 119 | */ 120 | static set(name: string | string[], value?: any | any[], store?: string, callback?: Function); 121 | 122 | /** Get an array of the keys in the context store */ 123 | static keys(): Array; 124 | /** Get an array of the keys in the context store */ 125 | static keys(store: string): Array; 126 | /** Get an array of the keys in the context store */ 127 | static keys(callback: Function); 128 | /** Get an array of the keys in the context store */ 129 | static keys(store: string, callback: Function); 130 | } 131 | declare class flow { 132 | /** 133 | * Get one or multiple values from context (synchronous). 134 | * @param name - Name of context variable 135 | */ 136 | static get(name: string | string[]); 137 | /** 138 | * Get one or multiple values from context (asynchronous). 139 | * @param name - Name (or array of names) to get from context 140 | * @param {function} callback - (optional) Callback function (`(err,value) => {}`) 141 | */ 142 | static get(name: string | string[], callback: Function); 143 | /** 144 | * Get one or multiple values from context (synchronous). 145 | * @param name - Name (or array of names) to get from context 146 | * @param store - Name of context store 147 | */ 148 | static get(name: string | string[], store: string); 149 | /** 150 | * Get one or multiple values from context (asynchronous). 151 | * @param name - Name (or array of names) to get from context 152 | * @param store - Name of context store 153 | * @param {function} callback - (optional) Callback function (`(err,value) => {}`) 154 | */ 155 | static get(name: string | string[], store: string, callback: Function); 156 | 157 | 158 | /** 159 | * Set one or multiple values in context (synchronous). 160 | * @param name - Name (or array of names) to set in context 161 | * @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed. 162 | */ 163 | static set(name: string | string[], value?: any | any[]); 164 | /** 165 | * Set one or multiple values in context (asynchronous). 166 | * @param name - Name (or array of names) to set in context 167 | * @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed. 168 | * @param callback - (optional) Callback function (`(err) => {}`) 169 | */ 170 | static set(name: string | string[], value?: any | any[], callback?: Function); 171 | /** 172 | * Set one or multiple values in context (synchronous). 173 | * @param name - Name (or array of names) to set in context 174 | * @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed. 175 | * @param store - (optional) Name of context store 176 | */ 177 | static set(name: string | string[], value?: any | any[], store?: string); 178 | /** 179 | * Set one or multiple values in context (asynchronous). 180 | * @param name - Name (or array of names) to set in context 181 | * @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed. 182 | * @param store - (optional) Name of context store 183 | * @param callback - (optional) Callback function (`(err) => {}`) 184 | */ 185 | static set(name: string | string[], value?: any | any[], store?: string, callback?: Function); 186 | 187 | /** Get an array of the keys in the context store */ 188 | static keys(): Array; 189 | /** Get an array of the keys in the context store */ 190 | static keys(store: string): Array; 191 | /** Get an array of the keys in the context store */ 192 | static keys(callback: Function); 193 | /** Get an array of the keys in the context store */ 194 | static keys(store: string, callback: Function); 195 | } 196 | 197 | // @ts-ignore 198 | declare class global { 199 | /** 200 | * Get one or multiple values from context (synchronous). 201 | * @param name - Name of context variable 202 | */ 203 | static get(name: string | string[]); 204 | /** 205 | * Get one or multiple values from context (asynchronous). 206 | * @param name - Name (or array of names) to get from context 207 | * @param {function} callback - (optional) Callback function (`(err,value) => {}`) 208 | */ 209 | static get(name: string | string[], callback: Function); 210 | /** 211 | * Get one or multiple values from context (synchronous). 212 | * @param name - Name (or array of names) to get from context 213 | * @param store - Name of context store 214 | */ 215 | static get(name: string | string[], store: string); 216 | /** 217 | * Get one or multiple values from context (asynchronous). 218 | * @param name - Name (or array of names) to get from context 219 | * @param store - Name of context store 220 | * @param {function} callback - (optional) Callback function (`(err,value) => {}`) 221 | */ 222 | static get(name: string | string[], store: string, callback: Function); 223 | 224 | 225 | /** 226 | * Set one or multiple values in context (synchronous). 227 | * @param name - Name (or array of names) to set in context 228 | * @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed. 229 | */ 230 | static set(name: string | string[], value?: any | any[]); 231 | /** 232 | * Set one or multiple values in context (asynchronous). 233 | * @param name - Name (or array of names) to set in context 234 | * @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed. 235 | * @param callback - (optional) Callback function (`(err) => {}`) 236 | */ 237 | static set(name: string | string[], value?: any | any[], callback?: Function); 238 | /** 239 | * Set one or multiple values in context (synchronous). 240 | * @param name - Name (or array of names) to set in context 241 | * @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed. 242 | * @param store - (optional) Name of context store 243 | */ 244 | static set(name: string | string[], value?: any | any[], store?: string); 245 | /** 246 | * Set one or multiple values in context (asynchronous). 247 | * @param name - Name (or array of names) to set in context 248 | * @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed. 249 | * @param store - (optional) Name of context store 250 | * @param callback - (optional) Callback function (`(err) => {}`) 251 | */ 252 | static set(name: string | string[], value?: any | any[], store?: string, callback?: Function); 253 | 254 | /** Get an array of the keys in the context store */ 255 | static keys(): Array; 256 | /** Get an array of the keys in the context store */ 257 | static keys(store: string): Array; 258 | /** Get an array of the keys in the context store */ 259 | static keys(callback: Function); 260 | /** Get an array of the keys in the context store */ 261 | static keys(store: string, callback: Function); 262 | } 263 | 264 | // (string & {}) is a workaround for offering string type completion without enforcing it. See https://github.com/microsoft/TypeScript/issues/29729#issuecomment-567871939 265 | type NR_ENV_NAME_STRING = 'NR_NODE_ID'|'NR_NODE_NAME'|'NR_NODE_PATH'|'NR_GROUP_ID'|'NR_GROUP_NAME'|'NR_FLOW_ID'|'NR_FLOW_NAME'|'NR_SUBFLOW_ID'|'NR_SUBFLOW_NAME'|'NR_SUBFLOW_PATH' | (string & {}) 266 | declare class env { 267 | /** 268 | * Get an environment variable value defined in the OS, or in the global/flow/subflow/group environment variables. 269 | * 270 | * Predefined node-red variables... 271 | * * `NR_NODE_ID` - the ID of the node 272 | * * `NR_NODE_NAME` - the Name of the node 273 | * * `NR_NODE_PATH` - the Path of the node 274 | * * `NR_GROUP_ID` - the ID of the containing group 275 | * * `NR_GROUP_NAME` - the Name of the containing group 276 | * * `NR_FLOW_ID` - the ID of the flow the node is on 277 | * * `NR_FLOW_NAME` - the Name of the flow the node is on 278 | * * `NR_SUBFLOW_ID` - the ID of the subflow the node is in 279 | * * `NR_SUBFLOW_NAME` - the Name of the subflow the node is in 280 | * * `NR_SUBFLOW_PATH` - the Path of the subflow the node is in 281 | * @param name - The name of the environment variable 282 | * @example 283 | * ```const flowName = env.get("NR_FLOW_NAME") // get the name of the flow``` 284 | * @example 285 | * ```const systemHomeDir = env.get("HOME") // get the user's home directory``` 286 | * @example 287 | * ```const systemHomeDir = env.get("LABEL1") // get the value of a global/flow/subflow/group defined variable named "LABEL1"``` 288 | */ 289 | static get(name:NR_ENV_NAME_STRING) :any; 290 | } 291 | --------------------------------------------------------------------------------