utils.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. //@ts-check
  6. const fs = require('fs');
  7. const path = require('path');
  8. const cp = require('child_process');
  9. const esbuild = require('esbuild');
  10. const alias = require('esbuild-plugin-alias');
  11. const REPO_ROOT = path.join(__dirname, '..');
  12. /**
  13. * @param {string} dirname
  14. */
  15. function ensureDir(dirname) {
  16. /** @type {string[]} */
  17. const dirs = [];
  18. while (dirname.length > REPO_ROOT.length) {
  19. dirs.push(dirname);
  20. dirname = path.dirname(dirname);
  21. }
  22. dirs.reverse();
  23. dirs.forEach(function (dir) {
  24. try {
  25. fs.mkdirSync(dir);
  26. } catch (err) {}
  27. });
  28. }
  29. /**
  30. * Copy a file.
  31. *
  32. * @param {string} _source
  33. * @param {string} _destination
  34. */
  35. function copyFile(_source, _destination) {
  36. const source = path.join(REPO_ROOT, _source);
  37. const destination = path.join(REPO_ROOT, _destination);
  38. ensureDir(path.dirname(destination));
  39. fs.writeFileSync(destination, fs.readFileSync(source));
  40. console.log(`Copied ${_source} to ${_destination}`);
  41. }
  42. exports.copyFile = copyFile;
  43. /**
  44. * Remove a directory and all its contents.
  45. *
  46. * @param {string} _dirPath
  47. */
  48. function removeDir(_dirPath) {
  49. const dirPath = path.join(REPO_ROOT, _dirPath);
  50. if (!fs.existsSync(dirPath)) {
  51. return;
  52. }
  53. rmDir(dirPath);
  54. console.log(`Deleted ${_dirPath}`);
  55. /**
  56. * @param {string} dirPath
  57. */
  58. function rmDir(dirPath) {
  59. const entries = fs.readdirSync(dirPath);
  60. for (const entry of entries) {
  61. const filePath = path.join(dirPath, entry);
  62. if (fs.statSync(filePath).isFile()) {
  63. fs.unlinkSync(filePath);
  64. } else {
  65. rmDir(filePath);
  66. }
  67. }
  68. fs.rmdirSync(dirPath);
  69. }
  70. }
  71. exports.removeDir = removeDir;
  72. /**
  73. * Launch the typescript compiler synchronously over a project.
  74. *
  75. * @param {string} _projectPath
  76. */
  77. function tsc(_projectPath) {
  78. const projectPath = path.join(REPO_ROOT, _projectPath);
  79. console.log(`Launching compiler at ${_projectPath}...`);
  80. cp.spawnSync(
  81. process.execPath,
  82. [path.join(__dirname, '../node_modules/typescript/lib/tsc.js'), '-p', projectPath],
  83. { stdio: 'inherit' }
  84. );
  85. console.log(`Compiled ${_projectPath}`);
  86. }
  87. exports.tsc = tsc;
  88. /**
  89. * Launch prettier on a specific file.
  90. *
  91. * @param {string} _filePath
  92. */
  93. function prettier(_filePath) {
  94. const filePath = path.join(REPO_ROOT, _filePath);
  95. cp.spawnSync(
  96. process.execPath,
  97. [path.join(__dirname, '../node_modules/prettier/bin-prettier.js'), '--write', filePath],
  98. { stdio: 'inherit' }
  99. );
  100. console.log(`Ran prettier over ${_filePath}`);
  101. }
  102. exports.prettier = prettier;
  103. /**
  104. * Transform an external .d.ts file to an internal .d.ts file
  105. *
  106. * @param {string} _source
  107. * @param {string} _destination
  108. * @param {string} namespace
  109. */
  110. function dts(_source, _destination, namespace) {
  111. const source = path.join(REPO_ROOT, _source);
  112. const destination = path.join(REPO_ROOT, _destination);
  113. const lines = fs
  114. .readFileSync(source)
  115. .toString()
  116. .split(/\r\n|\r|\n/);
  117. let result = [
  118. `/*---------------------------------------------------------------------------------------------`,
  119. ` * Copyright (c) Microsoft Corporation. All rights reserved.`,
  120. ` * Licensed under the MIT License. See License.txt in the project root for license information.`,
  121. ` *--------------------------------------------------------------------------------------------*/`,
  122. ``,
  123. `declare namespace ${namespace} {`
  124. ];
  125. for (let line of lines) {
  126. if (/^import/.test(line)) {
  127. continue;
  128. }
  129. if (line === 'export {};') {
  130. continue;
  131. }
  132. line = line.replace(/ /g, '\t');
  133. line = line.replace(/declare /g, '');
  134. if (line.length > 0) {
  135. line = `\t${line}`;
  136. result.push(line);
  137. }
  138. }
  139. result.push(`}`);
  140. result.push(``);
  141. ensureDir(path.dirname(destination));
  142. fs.writeFileSync(destination, result.join('\n'));
  143. prettier(_destination);
  144. }
  145. exports.dts = dts;
  146. /**
  147. * @param {import('esbuild').BuildOptions} options
  148. */
  149. function build(options) {
  150. esbuild.build(options).then((result) => {
  151. if (result.errors.length > 0) {
  152. console.error(result.errors);
  153. }
  154. if (result.warnings.length > 0) {
  155. console.error(result.warnings);
  156. }
  157. });
  158. }
  159. exports.build = build;
  160. /**
  161. * @param {{
  162. * base: string;
  163. * entryPoints: string[];
  164. * external: string[];
  165. * }} options
  166. */
  167. function buildESM(options) {
  168. build({
  169. entryPoints: options.entryPoints.map((e) => `${options.base}/${e}`),
  170. bundle: true,
  171. target: 'esnext',
  172. format: 'esm',
  173. define: {
  174. AMD: 'false'
  175. },
  176. banner: {
  177. js: bundledFileHeader
  178. },
  179. external: options.external,
  180. outbase: `${options.base}/src`,
  181. outdir: `${options.base}/release/esm/`,
  182. plugins: [
  183. alias({
  184. 'vscode-nls': path.join(__dirname, 'fillers/vscode-nls.ts')
  185. })
  186. ]
  187. });
  188. }
  189. exports.buildESM = buildESM;
  190. /**
  191. * @param {{
  192. * base: string;
  193. * entryPoints: string[];
  194. * external: string[];
  195. * }} options
  196. */
  197. function buildESM2(options) {
  198. build({
  199. entryPoints: options.entryPoints,
  200. bundle: true,
  201. target: 'esnext',
  202. format: 'esm',
  203. define: {
  204. AMD: 'false'
  205. },
  206. banner: {
  207. js: bundledFileHeader
  208. },
  209. external: options.external,
  210. outbase: `src/${options.base}`,
  211. outdir: `out/release/${options.base}/esm/`,
  212. plugins: [
  213. alias({
  214. 'vscode-nls': path.join(__dirname, 'fillers/vscode-nls.ts')
  215. })
  216. ]
  217. });
  218. }
  219. exports.buildESM2 = buildESM2;
  220. /**
  221. * @param {'dev'|'min'} type
  222. * @param {{
  223. * base: string;
  224. * entryPoint: string;
  225. * amdModuleId: string;
  226. * amdDependencies?: string[];
  227. * }} options
  228. */
  229. function buildOneAMD(type, options) {
  230. /** @type {import('esbuild').BuildOptions} */
  231. const opts = {
  232. entryPoints: [`${options.base}/${options.entryPoint}`],
  233. bundle: true,
  234. target: 'esnext',
  235. format: 'iife',
  236. define: {
  237. AMD: 'true'
  238. },
  239. globalName: 'moduleExports',
  240. banner: {
  241. js: `${bundledFileHeader}define("${options.amdModuleId}",[${(options.amdDependencies || [])
  242. .map((dep) => `"${dep}"`)
  243. .join(',')}],()=>{`
  244. },
  245. footer: {
  246. js: 'return moduleExports;\n});'
  247. },
  248. outbase: `${options.base}/src`,
  249. outdir: `${options.base}/release/${type}/`,
  250. plugins: [
  251. alias({
  252. 'vscode-nls': path.join(__dirname, '../build/fillers/vscode-nls.ts'),
  253. 'monaco-editor-core': path.join(__dirname, '../build/fillers/monaco-editor-core-amd.ts')
  254. })
  255. ]
  256. };
  257. if (type === 'min') {
  258. opts.minify = true;
  259. }
  260. build(opts);
  261. }
  262. /**
  263. * @param {{
  264. * base: string;
  265. * entryPoint: string;
  266. * amdModuleId: string;
  267. * amdDependencies?: string[];
  268. * }} options
  269. */
  270. function buildAMD(options) {
  271. buildOneAMD('dev', options);
  272. buildOneAMD('min', options);
  273. }
  274. exports.buildAMD = buildAMD;
  275. /**
  276. * @param {'dev'|'min'} type
  277. * @param {{
  278. * base: string;
  279. * entryPoint: string;
  280. * amdModuleId: string;
  281. * amdDependencies?: string[];
  282. * }} options
  283. */
  284. function buildOneAMD2(type, options) {
  285. /** @type {import('esbuild').BuildOptions} */
  286. const opts = {
  287. entryPoints: [options.entryPoint],
  288. bundle: true,
  289. target: 'esnext',
  290. format: 'iife',
  291. define: {
  292. AMD: 'true'
  293. },
  294. globalName: 'moduleExports',
  295. banner: {
  296. js: `${bundledFileHeader}define("${options.amdModuleId}",[${(options.amdDependencies || [])
  297. .map((dep) => `"${dep}"`)
  298. .join(',')}],()=>{`
  299. },
  300. footer: {
  301. js: 'return moduleExports;\n});'
  302. },
  303. outbase: `src/${options.base}`,
  304. outdir: `out/release/${options.base}/${type}/`,
  305. plugins: [
  306. alias({
  307. 'vscode-nls': path.join(__dirname, '../build/fillers/vscode-nls.ts'),
  308. 'monaco-editor-core': path.join(__dirname, '../build/fillers/monaco-editor-core-amd.ts')
  309. })
  310. ]
  311. };
  312. if (type === 'min') {
  313. opts.minify = true;
  314. }
  315. build(opts);
  316. }
  317. /**
  318. * @param {{
  319. * base: string;
  320. * entryPoint: string;
  321. * amdModuleId: string;
  322. * amdDependencies?: string[];
  323. * }} options
  324. */
  325. function buildAMD2(options) {
  326. buildOneAMD2('dev', options);
  327. buildOneAMD2('min', options);
  328. }
  329. exports.buildAMD2 = buildAMD2;
  330. function getGitVersion() {
  331. const git = path.join(REPO_ROOT, '.git');
  332. const headPath = path.join(git, 'HEAD');
  333. let head;
  334. try {
  335. head = fs.readFileSync(headPath, 'utf8').trim();
  336. } catch (e) {
  337. return void 0;
  338. }
  339. if (/^[0-9a-f]{40}$/i.test(head)) {
  340. return head;
  341. }
  342. const refMatch = /^ref: (.*)$/.exec(head);
  343. if (!refMatch) {
  344. return void 0;
  345. }
  346. const ref = refMatch[1];
  347. const refPath = path.join(git, ref);
  348. try {
  349. return fs.readFileSync(refPath, 'utf8').trim();
  350. } catch (e) {
  351. // noop
  352. }
  353. const packedRefsPath = path.join(git, 'packed-refs');
  354. let refsRaw;
  355. try {
  356. refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim();
  357. } catch (e) {
  358. return void 0;
  359. }
  360. const refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm;
  361. let refsMatch;
  362. const refs = {};
  363. while ((refsMatch = refsRegex.exec(refsRaw))) {
  364. refs[refsMatch[2]] = refsMatch[1];
  365. }
  366. return refs[ref];
  367. }
  368. const bundledFileHeader = (() => {
  369. const sha1 = getGitVersion();
  370. const semver = require('../package.json').version;
  371. const headerVersion = semver + '(' + sha1 + ')';
  372. const BUNDLED_FILE_HEADER = [
  373. '/*!-----------------------------------------------------------------------------',
  374. ' * Copyright (c) Microsoft Corporation. All rights reserved.',
  375. ' * Version: ' + headerVersion,
  376. ' * Released under the MIT license',
  377. ' * https://github.com/microsoft/monaco-editor/blob/main/LICENSE.txt',
  378. ' *-----------------------------------------------------------------------------*/',
  379. ''
  380. ].join('\n');
  381. return BUNDLED_FILE_HEADER;
  382. })();
  383. exports.bundledFileHeader = bundledFileHeader;