releaseMetadata.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See LICENSE in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. //@ts-check
  6. const glob = require('glob');
  7. const path = require('path');
  8. const fs = require('fs');
  9. const { REPO_ROOT } = require('./utils');
  10. const { ensureDir } = require('./fs');
  11. const customFeatureLabels = {
  12. 'vs/editor/browser/controller/coreCommands': 'coreCommands',
  13. 'vs/editor/contrib/caretOperations/caretOperations': 'caretOperations',
  14. 'vs/editor/contrib/caretOperations/transpose': 'transpose',
  15. 'vs/editor/contrib/colorPicker/colorDetector': 'colorDetector',
  16. 'vs/editor/contrib/rename/onTypeRename': 'onTypeRename',
  17. 'vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition': 'gotoSymbol',
  18. 'vs/editor/contrib/snippet/snippetController2': 'snippets',
  19. 'vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess': 'gotoLine',
  20. 'vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess': 'quickCommand',
  21. 'vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess': 'quickOutline',
  22. 'vs/editor/standalone/browser/quickAccess/standaloneHelpQuickAccess': 'quickHelp'
  23. };
  24. /**
  25. * @returns { Promise<{ label: string; entry: string; }[]> }
  26. */
  27. function getBasicLanguages() {
  28. return new Promise((resolve, reject) => {
  29. glob(
  30. './release/esm/vs/basic-languages/*/*.contribution.js',
  31. { cwd: path.dirname(__dirname) },
  32. (err, files) => {
  33. if (err) {
  34. reject(err);
  35. return;
  36. }
  37. resolve(
  38. files.map((file) => {
  39. const entry = file.substring('./release/esm/'.length).replace(/\.js$/, '');
  40. const label = path.basename(file).replace(/\.contribution\.js$/, '');
  41. return {
  42. label: label,
  43. entry: entry
  44. };
  45. })
  46. );
  47. }
  48. );
  49. });
  50. }
  51. /**
  52. * @returns { Promise<string[]> }
  53. */
  54. function readAdvancedLanguages() {
  55. return new Promise((resolve, reject) => {
  56. glob(
  57. './release/esm/vs/language/*/monaco.contribution.js',
  58. { cwd: path.dirname(__dirname) },
  59. (err, files) => {
  60. if (err) {
  61. reject(err);
  62. return;
  63. }
  64. resolve(
  65. files
  66. .map((file) => file.substring('./release/esm/vs/language/'.length))
  67. .map((file) => file.substring(0, file.length - '/monaco.contribution.js'.length))
  68. );
  69. }
  70. );
  71. });
  72. }
  73. /**
  74. * @returns { Promise<{ label: string; entry: string; worker: { id: string; entry: string; }; }[]> }
  75. */
  76. function getAdvancedLanguages() {
  77. return readAdvancedLanguages().then((languages) => {
  78. let result = [];
  79. for (const lang of languages) {
  80. let shortLang = lang === 'typescript' ? 'ts' : lang;
  81. const entry = `vs/language/${lang}/monaco.contribution`;
  82. checkFileExists(entry);
  83. const workerId = `vs/language/${lang}/${shortLang}Worker`;
  84. const workerEntry = `vs/language/${lang}/${shortLang}.worker`;
  85. checkFileExists(workerEntry);
  86. result.push({
  87. label: lang,
  88. entry: entry,
  89. worker: {
  90. id: workerId,
  91. entry: workerEntry
  92. }
  93. });
  94. }
  95. return result;
  96. });
  97. function checkFileExists(moduleName) {
  98. const filePath = path.join(REPO_ROOT, 'release/esm', `${moduleName}.js`);
  99. if (!fs.existsSync(filePath)) {
  100. console.error(`Could not find ${filePath}.`);
  101. process.exit(1);
  102. }
  103. }
  104. }
  105. function generateMetadata() {
  106. return Promise.all([getBasicLanguages(), getAdvancedLanguages()]).then(
  107. ([basicLanguages, advancedLanguages]) => {
  108. basicLanguages.sort(strcmp);
  109. advancedLanguages.sort(strcmp);
  110. let i = 0,
  111. len = basicLanguages.length;
  112. let j = 0,
  113. lenJ = advancedLanguages.length;
  114. let languages = [];
  115. while (i < len || j < lenJ) {
  116. if (i < len && j < lenJ) {
  117. if (basicLanguages[i].label === advancedLanguages[j].label) {
  118. let entry = [];
  119. entry.push(basicLanguages[i].entry);
  120. entry.push(advancedLanguages[j].entry);
  121. languages.push({
  122. label: basicLanguages[i].label,
  123. entry: entry,
  124. worker: advancedLanguages[j].worker
  125. });
  126. i++;
  127. j++;
  128. } else if (basicLanguages[i].label < advancedLanguages[j].label) {
  129. languages.push(basicLanguages[i]);
  130. i++;
  131. } else {
  132. languages.push(advancedLanguages[j]);
  133. j++;
  134. }
  135. } else if (i < len) {
  136. languages.push(basicLanguages[i]);
  137. i++;
  138. } else {
  139. languages.push(advancedLanguages[j]);
  140. j++;
  141. }
  142. }
  143. const features = getFeatures();
  144. const dtsContents = `
  145. /*!----------------------------------------------------------------
  146. * Copyright (c) Microsoft Corporation. All rights reserved.
  147. * Released under the MIT license
  148. * https://github.com/microsoft/monaco-editor/blob/main/LICENSE.txt
  149. *----------------------------------------------------------------*/
  150. export interface IWorkerDefinition {
  151. id: string;
  152. entry: string;
  153. }
  154. export interface IFeatureDefinition {
  155. label: string;
  156. entry: string | string[] | undefined;
  157. worker?: IWorkerDefinition;
  158. }
  159. export const features: IFeatureDefinition[];
  160. export const languages: IFeatureDefinition[];
  161. export type EditorLanguage = ${languages.map((el) => `'${el.label}'`).join(' | ')};
  162. export type EditorFeature = ${features.map((el) => `'${el.label}'`).join(' | ')};
  163. export type NegatedEditorFeature = ${features.map((el) => `'!${el.label}'`).join(' | ')};
  164. `;
  165. const dtsDestination = path.join(REPO_ROOT, 'release/esm/metadata.d.ts');
  166. ensureDir(path.dirname(dtsDestination));
  167. fs.writeFileSync(dtsDestination, dtsContents.replace(/\r\n/g, '\n'));
  168. const jsContents = `
  169. exports.features = ${JSON.stringify(features, null, ' ')};
  170. exports.languages = ${JSON.stringify(languages, null, ' ')};
  171. `;
  172. const jsDestination = path.join(REPO_ROOT, 'release/esm/metadata.js');
  173. ensureDir(path.dirname(jsDestination));
  174. fs.writeFileSync(jsDestination, jsContents.replace(/\r\n/g, '\n'));
  175. }
  176. );
  177. }
  178. exports.generateMetadata = generateMetadata;
  179. /**
  180. * @tyoe {string} a
  181. * @tyoe {string} b
  182. */
  183. function strcmp(a, b) {
  184. if (a < b) {
  185. return -1;
  186. }
  187. if (a > b) {
  188. return 1;
  189. }
  190. return 0;
  191. }
  192. /**
  193. * @returns {{label:string;entry:string|string[];}[]}
  194. */
  195. function getFeatures() {
  196. const skipImports = [
  197. 'vs/editor/browser/widget/codeEditorWidget',
  198. 'vs/editor/browser/widget/diffEditorWidget',
  199. 'vs/editor/browser/widget/diffNavigator',
  200. 'vs/editor/common/standaloneStrings',
  201. 'vs/editor/contrib/tokenization/tokenization',
  202. 'vs/editor/editor.all',
  203. 'vs/base/browser/ui/codicons/codiconStyles',
  204. 'vs/editor/contrib/gotoSymbol/documentSymbols'
  205. ];
  206. /** @type {string[]} */
  207. let features = [];
  208. const files =
  209. fs.readFileSync(path.join(REPO_ROOT, 'release/esm/vs/editor/edcore.main.js')).toString() +
  210. fs.readFileSync(path.join(REPO_ROOT, 'release/esm/vs/editor/editor.all.js')).toString();
  211. files.split(/\r\n|\n/).forEach((line) => {
  212. const m = line.match(/import '([^']+)'/);
  213. if (m) {
  214. const tmp = path.posix.join('vs/editor', m[1]).replace(/\.js$/, '');
  215. if (skipImports.indexOf(tmp) === -1) {
  216. features.push(tmp);
  217. }
  218. }
  219. });
  220. /** @type {{label:string;entry:any;}[]} */
  221. let result = features.map((feature) => {
  222. /** @type {string} */ let label;
  223. if (customFeatureLabels[feature]) {
  224. label = customFeatureLabels[feature];
  225. } else {
  226. const m1 = feature.match(/^vs\/editor\/contrib\/([^\/]+)/);
  227. if (m1) {
  228. // for editor/contrib features, use the first segment
  229. label = m1[1];
  230. } else {
  231. // for everything else, use the last segment folder
  232. label = path.basename(path.dirname(feature));
  233. }
  234. }
  235. return {
  236. label: label,
  237. entry: feature
  238. };
  239. });
  240. result.sort((a, b) => {
  241. const labelCmp = strcmp(a.label, b.label);
  242. if (labelCmp === 0) {
  243. return strcmp(a.entry, b.entry);
  244. }
  245. return labelCmp;
  246. });
  247. for (let i = 0; i < result.length; i++) {
  248. if (i + 1 < result.length && result[i].label === result[i + 1].label) {
  249. if (typeof result[i].entry === 'string') {
  250. result[i].entry = [result[i].entry];
  251. }
  252. result[i].entry.push(result[i + 1].entry);
  253. result.splice(i + 1, 1);
  254. }
  255. }
  256. return result;
  257. }