esbuild.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.wrapperNVueAppStyles = exports.uniEsbuildPlugin = void 0;
  7. const path_1 = __importDefault(require("path"));
  8. const fs_extra_1 = __importDefault(require("fs-extra"));
  9. const debug_1 = __importDefault(require("debug"));
  10. const uni_cli_shared_1 = require("@dcloudio/uni-cli-shared");
  11. const utils_1 = require("../../utils");
  12. const utils_2 = require("../utils");
  13. const appCss_1 = require("./appCss");
  14. const debugEsbuild = (0, debug_1.default)('uni:app-nvue-esbuild');
  15. const emittedHashMap = new WeakMap();
  16. function uniEsbuildPlugin({ appService, }) {
  17. let resolvedConfig;
  18. let buildOptions;
  19. const nvueOutputDir = (0, utils_1.nvueOutDir)();
  20. const outputDir = process.env.UNI_OUTPUT_DIR;
  21. let isFirst = true;
  22. return {
  23. name: 'uni:app-nvue-esbuild',
  24. enforce: 'post',
  25. configResolved(config) {
  26. buildOptions = {
  27. format: 'iife',
  28. target: 'es6',
  29. minify: config.build.minify ? true : false,
  30. banner: {
  31. js: `"use weex:vue";
  32. ${uni_cli_shared_1.polyfillCode}`,
  33. },
  34. bundle: true,
  35. write: false,
  36. plugins: [esbuildGlobalPlugin((0, utils_2.esbuildGlobals)(appService))],
  37. };
  38. resolvedConfig = config;
  39. emittedHashMap.set(resolvedConfig, new Map());
  40. },
  41. async writeBundle(_, bundle) {
  42. const entryPoints = [];
  43. const assets = [];
  44. Object.keys(bundle).forEach((name) => {
  45. const chunk = bundle[name];
  46. if (chunk.type === 'chunk' &&
  47. chunk.facadeModuleId &&
  48. chunk.facadeModuleId.endsWith('.nvue')) {
  49. entryPoints.push(name);
  50. }
  51. else if (chunk.type === 'asset') {
  52. assets.push(name);
  53. }
  54. });
  55. // 仅 nvueOutputDir 时 copy
  56. if (!appService) {
  57. assets.forEach((name) => {
  58. fs_extra_1.default.copySync(path_1.default.resolve(nvueOutputDir, name), path_1.default.resolve(outputDir, name), { overwrite: false });
  59. });
  60. }
  61. if (!entryPoints.length) {
  62. return;
  63. }
  64. const emittedHash = emittedHashMap.get(resolvedConfig);
  65. const changedFiles = [];
  66. if (buildAppCss()) {
  67. changedFiles.push(uni_cli_shared_1.APP_CONFIG_SERVICE);
  68. }
  69. debugEsbuild('start', entryPoints.length, entryPoints);
  70. for (const filename of entryPoints) {
  71. await buildNVuePage(filename, buildOptions).then((code) => {
  72. const outputFileHash = (0, uni_cli_shared_1.hash)(code);
  73. if (emittedHash.get(filename) !== outputFileHash) {
  74. changedFiles.push(filename);
  75. emittedHash.set(filename, outputFileHash);
  76. return fs_extra_1.default.outputFile(path_1.default.resolve(outputDir, filename), code);
  77. }
  78. });
  79. }
  80. if (!isFirst && changedFiles.length) {
  81. process.env[changedFiles.includes(uni_cli_shared_1.APP_CONFIG_SERVICE)
  82. ? 'UNI_APP_CHANGED_FILES'
  83. : 'UNI_APP_CHANGED_PAGES'] = JSON.stringify(changedFiles);
  84. }
  85. debugEsbuild('end');
  86. isFirst = false;
  87. },
  88. };
  89. }
  90. exports.uniEsbuildPlugin = uniEsbuildPlugin;
  91. /**
  92. * 将 nvue 全局 css 样式注入 app-config-service.js
  93. * @returns
  94. */
  95. function buildAppCss() {
  96. const appCssJsFilename = path_1.default.join((0, utils_1.nvueOutDir)(), appCss_1.APP_CSS_JS);
  97. if (!fs_extra_1.default.existsSync(appCssJsFilename)) {
  98. return;
  99. }
  100. const appCssJsCode = fs_extra_1.default.readFileSync(appCssJsFilename, 'utf8');
  101. const appCssJsFn = new Function('module',
  102. // vite build.target为esnext时, 生成的代码没有export default
  103. appCssJsCode.includes('export default')
  104. ? appCssJsCode.replace(`export default`, `module.exports=`)
  105. : appCssJsCode.replace(`exports`, `module.exports`));
  106. const module = { exports: { styles: [] } };
  107. appCssJsFn(module);
  108. const appCssJsonCode = JSON.stringify(module.exports.styles);
  109. if (process.env.UNI_NVUE_APP_STYLES === appCssJsonCode) {
  110. return;
  111. }
  112. process.env.UNI_NVUE_APP_STYLES = appCssJsonCode;
  113. // 首次 build 时,可能还没生成 app-config-service 的文件,故仅写入环境变量
  114. const appConfigServiceFilename = path_1.default.join(process.env.UNI_OUTPUT_DIR, uni_cli_shared_1.APP_CONFIG_SERVICE);
  115. if (!fs_extra_1.default.existsSync(appConfigServiceFilename)) {
  116. return;
  117. }
  118. const appConfigServiceCode = fs_extra_1.default.readFileSync(appConfigServiceFilename, 'utf8');
  119. fs_extra_1.default.writeFileSync(appConfigServiceFilename, wrapperNVueAppStyles(appConfigServiceCode));
  120. return true;
  121. }
  122. function buildNVuePage(filename, options) {
  123. return (0, uni_cli_shared_1.transformWithEsbuild)(`import App from './${filename}'
  124. const webview = plus.webview.currentWebview()
  125. if(webview){
  126. const __pageId = parseInt(webview.id)
  127. const __pagePath = '${(0, uni_cli_shared_1.removeExt)(filename)}'
  128. let __pageQuery = {}
  129. try{ __pageQuery = JSON.parse(webview.__query__) }catch(e){}
  130. App.mpType = 'page'
  131. const app = Vue.createPageApp(App,{$store:getApp({allowDefault:true}).$store,__pageId,__pagePath,__pageQuery})
  132. app.provide('__globalStyles', Vue.useCssStyles([...__uniConfig.styles, ...(App.styles||[])]))
  133. app.mount('#root')
  134. }`, path_1.default.join((0, utils_1.nvueOutDir)(), 'main.js'), options).then((res) => {
  135. if (res.outputFiles) {
  136. return res.outputFiles[0].text;
  137. }
  138. return '';
  139. });
  140. }
  141. function esbuildGlobalPlugin(options) {
  142. const keys = Object.keys(options);
  143. return {
  144. name: 'global',
  145. setup(build) {
  146. keys.forEach((key) => {
  147. const namespace = key + '-ns';
  148. build.onResolve({ filter: new RegExp('^' + key + '$') }, ({ path }) => {
  149. return {
  150. path,
  151. namespace,
  152. };
  153. });
  154. build.onLoad({ filter: /.*/, namespace }, () => ({
  155. contents: `module.exports = ${options[key]}`,
  156. loader: 'js',
  157. }));
  158. });
  159. },
  160. };
  161. }
  162. function wrapperNVueAppStyles(code) {
  163. return code.replace(/__uniConfig.styles=(.*);\/\/styles/, `__uniConfig.styles=${process.env.UNI_NVUE_APP_STYLES || '[]'};//styles`);
  164. }
  165. exports.wrapperNVueAppStyles = wrapperNVueAppStyles;