releaseMetadata.ts 8.2 KB

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