"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.watcherIgnoreRegExp = void 0; function _react() { const data = _interopRequireDefault(require("react")); _react = function _react() { return data; }; return data; } function _path() { const data = require("path"); _path = function _path() { return data; }; return data; } function _fs() { const data = require("fs"); _fs = function _fs() { return data; }; return data; } function _mkdirp() { const data = _interopRequireDefault(require("mkdirp")); _mkdirp = function _mkdirp() { return data; }; return data; } function _chokidar() { const data = _interopRequireDefault(require("chokidar")); _chokidar = function _chokidar() { return data; }; return data; } function _assert() { const data = _interopRequireDefault(require("assert")); _assert = function _assert() { return data; }; return data; } function _chalk() { const data = _interopRequireDefault(require("chalk")); _chalk = function _chalk() { return data; }; return data; } function _lodash() { const data = require("lodash"); _lodash = function _lodash() { return data; }; return data; } function _mustache() { const data = _interopRequireDefault(require("mustache")); _mustache = function _mustache() { return data; }; return data; } function _umiUtils() { const data = require("umi-utils"); _umiUtils = function _umiUtils() { return data; }; return data; } var _stripJSONQuote = _interopRequireDefault(require("./routes/stripJSONQuote")); var _routesToJSON = _interopRequireDefault(require("./routes/routesToJSON")); var _importsToStr = _interopRequireDefault(require("./importsToStr")); var _constants = require("./constants"); var _getHtmlGenerator = _interopRequireDefault(require("./plugins/commands/getHtmlGenerator")); var _htmlToJSX = _interopRequireDefault(require("./htmlToJSX")); var _getRoutePaths = _interopRequireDefault(require("./routes/getRoutePaths")); var _writeContent = _interopRequireDefault(require("./utils/writeContent.js")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const debug = require('debug')('umi:FilesGenerator'); const watcherIgnoreRegExp = /(^|[\/\\])(_mock.js$|\..)/; exports.watcherIgnoreRegExp = watcherIgnoreRegExp; function normalizePath(path, base = '/') { if (path.startsWith(base)) { path = path.replace(base, '/'); } return path; } class FilesGenerator { constructor(opts) { Object.keys(opts).forEach(key => { this[key] = opts[key]; }); this.routesContent = null; this.hasRebuildError = false; } generate() { debug('generate'); const paths = this.service.paths; const absTmpDirPath = paths.absTmpDirPath, tmpDirPath = paths.tmpDirPath; debug(`mkdir tmp dir: ${tmpDirPath}`); _mkdirp().default.sync(absTmpDirPath); this.generateFiles(); } createWatcher(path) { const watcher = _chokidar().default.watch(path, { ignored: watcherIgnoreRegExp, // ignore .dotfiles and _mock.js ignoreInitial: true }); watcher.on('all', (0, _lodash().throttle)((event, path) => { debug(`${event} ${path}`); this.rebuild(); }, 100)); return watcher; } watch() { if (process.env.WATCH_FILES === 'none') return; const _this$service = this.service, paths = _this$service.paths, singular = _this$service.config.singular; const layout = singular ? 'layout' : 'layouts'; let pageWatchers = [paths.absPagesPath, ..._constants.EXT_LIST.map(ext => (0, _path().join)(paths.absSrcPath, `${layout}/index${ext}`)), ..._constants.EXT_LIST.map(ext => (0, _path().join)(paths.absSrcPath, `app${ext}`))]; if (this.modifyPageWatcher) { pageWatchers = this.modifyPageWatcher(pageWatchers); } this.watchers = pageWatchers.map(p => { return this.createWatcher(p); }); process.on('SIGINT', () => { this.unwatch(); }); } unwatch() { if (this.watchers) { this.watchers.forEach(watcher => { watcher.close(); }); } } rebuild() { const _this$service2 = this.service, refreshBrowser = _this$service2.refreshBrowser, printError = _this$service2.printError; const isDev = process.env.NODE_ENV === 'development'; try { this.service.applyPlugins('onGenerateFiles', { args: { isRebuild: true } }); this.generateRouterJS(); this.generateEntry(); this.generateHistory(); if (this.hasRebuildError) { if (isDev) refreshBrowser(); this.hasRebuildError = false; } } catch (e) { // 向浏览器发送出错信息 if (isDev) printError([e.message]); this.hasRebuildError = true; this.routesContent = null; // why? debug(`Generate failed: ${e.message}`); debug(e); console.error(_chalk().default.red(e.message)); } } generateFiles() { this.service.applyPlugins('onGenerateFiles'); this.generateRouterJS(); this.generateEntry(); this.generateHistory(); } generateEntry() { const _this$service3 = this.service, paths = _this$service3.paths, config = _this$service3.config; // Generate umi.js const entryTpl = (0, _fs().readFileSync)(paths.defaultEntryTplPath, 'utf-8'); const initialRender = this.service.applyPlugins('modifyEntryRender', { initialValue: ` window.g_isBrowser = true; let props = {}; // Both support SSR and CSR if (window.g_useSSR) { // 如果开启服务端渲染则客户端组件初始化 props 使用服务端注入的数据 props = window.g_initialData; } else { const pathname = location.pathname; const activeRoute = findRoute(require('@@/router').routes, pathname); // 在客户端渲染前,执行 getInitialProps 方法 // 拿到初始数据 if (activeRoute && activeRoute.component && activeRoute.component.getInitialProps) { const initialProps = plugins.apply('modifyInitialProps', { initialValue: {}, }); props = activeRoute.component.getInitialProps ? await activeRoute.component.getInitialProps({ route: activeRoute, isServer: false, location, ...initialProps, }) : {}; } } const rootContainer = plugins.apply('rootContainer', { initialValue: React.createElement(require('./router').default, props), }); ReactDOM[window.g_useSSR ? 'hydrate' : 'render']( rootContainer, document.getElementById('${config.mountElementId}'), ); `.trim() }); const moduleBeforeRenderer = this.service.applyPlugins('addRendererWrapperWithModule', { initialValue: [] }).map((source, index) => { return { source, specifier: `moduleBeforeRenderer${index}` }; }); const plugins = this.service.applyPlugins('addRuntimePlugin', { initialValue: [] }).map(plugin => { return (0, _umiUtils().winPath)((0, _path().relative)(paths.absTmpDirPath, plugin)); }); if ((0, _umiUtils().findJS)(paths.absSrcPath, 'app')) { plugins.push('@/app'); } const validKeys = this.service.applyPlugins('addRuntimePluginKey', { initialValue: ['patchRoutes', 'render', 'rootContainer', 'modifyRouteProps', 'onRouteChange', 'modifyInitialProps', 'initialProps'] }); (0, _assert().default)((0, _lodash().uniq)(validKeys).length === validKeys.length, `Conflict keys found in [${validKeys.join(', ')}]`); let htmlTemplateMap = []; if (config.ssr) { const isProd = process.env.NODE_ENV === 'production'; const routePaths = (0, _getRoutePaths.default)(this.RoutesManager.routes); htmlTemplateMap = routePaths.map(routePath => { let ssrHtml = '<>'; const hg = (0, _getHtmlGenerator.default)(this.service, { chunksMap: { // TODO, for dynamic chunks // placeholder waiting manifest umi: [isProd ? '__UMI_SERVER__.js' : 'umi.js', isProd ? '__UMI_SERVER__.css' : 'umi.css'] }, headScripts: [{ content: 'window.g_useSSR=true;'.trim() }], scripts: [{ content: `window.g_initialData = \${stringify(props)};`.trim() }] }); const content = hg.getMatchedContent(normalizePath(routePath, config.base)); ssrHtml = (0, _htmlToJSX.default)(content).replace(`
`, `
{rootContainer}
`); return `'${routePath}': (${ssrHtml}),`; }); } const entryContent = _mustache().default.render(entryTpl, { globalVariables: !this.service.config.disableGlobalVariables, code: this.service.applyPlugins('addEntryCode', { initialValue: [] }).join('\n\n'), codeAhead: this.service.applyPlugins('addEntryCodeAhead', { initialValue: [] }).join('\n\n'), imports: (0, _importsToStr.default)(this.service.applyPlugins('addEntryImport', { initialValue: moduleBeforeRenderer })).join('\n'), importsAhead: (0, _importsToStr.default)(this.service.applyPlugins('addEntryImportAhead', { initialValue: [] })).join('\n'), polyfillImports: (0, _importsToStr.default)(this.service.applyPlugins('addEntryPolyfillImports', { initialValue: [] })).join('\n'), moduleBeforeRenderer, render: initialRender, plugins, validKeys, htmlTemplateMap: htmlTemplateMap.join('\n'), findRoutePath: (0, _umiUtils().winPath)(require.resolve('./findRoute')) }); (0, _writeContent.default)(paths.absLibraryJSPath, (0, _umiUtils().prettierFile)(`${entryContent.trim()}\n`)); } generateHistory() { const _this$service4 = this.service, paths = _this$service4.paths, config = _this$service4.config; const tpl = (0, _fs().readFileSync)(paths.defaultHistoryTplPath, 'utf-8'); const initialHistory = ` require('umi/lib/createHistory').default({ basename: window.routerBase, }) `.trim(); let history = this.service.applyPlugins('modifyEntryHistory', { initialValue: initialHistory }); if (config.ssr) { history = ` __IS_BROWSER ? ${initialHistory} : require('history').createMemoryHistory({ // for history object in dva initialEntries: [global.req ? global.req.url : '/'] }) `.trim(); } const content = _mustache().default.render(tpl, { globalVariables: !this.service.config.disableGlobalVariables, history }); (0, _writeContent.default)((0, _path().join)(paths.absTmpDirPath, 'history.js'), (0, _umiUtils().prettierFile)(`${content.trim()}\n`)); } generateRouterJS() { const paths = this.service.paths; const absRouterJSPath = paths.absRouterJSPath; this.RoutesManager.fetchRoutes(); const routesContent = this.getRouterJSContent(); // 避免文件写入导致不必要的 webpack 编译 if (this.routesContent !== routesContent) { (0, _fs().writeFileSync)(absRouterJSPath, (0, _umiUtils().prettierFile)(`${routesContent.trim()}\n`), 'utf-8'); this.routesContent = routesContent; this.service.applyPlugins('onRouteChange'); } } getRouterJSContent() { const paths = this.service.paths; const routerTpl = (0, _fs().readFileSync)(paths.defaultRouterTplPath, 'utf-8'); const routes = (0, _stripJSONQuote.default)(this.getRoutesJSON({ env: process.env.NODE_ENV })); const rendererWrappers = this.service.applyPlugins('addRendererWrapperWithComponent', { initialValue: [] }).map((source, index) => { return { source, specifier: `RendererWrapper${index}` }; }); const routerContent = this.getRouterContent(rendererWrappers); return _mustache().default.render(routerTpl, { globalVariables: !this.service.config.disableGlobalVariables, imports: (0, _importsToStr.default)(this.service.applyPlugins('addRouterImport', { initialValue: rendererWrappers })).join('\n'), importsAhead: (0, _importsToStr.default)(this.service.applyPlugins('addRouterImportAhead', { initialValue: [] })).join('\n'), routes, routerContent, RouterRootComponent: this.service.applyPlugins('modifyRouterRootComponent', { initialValue: 'DefaultRouter' }) }); } fixHtmlSuffix(routes) { routes.forEach(route => { if (route.routes) { route.path = `${route.path}(.html)?`; this.fixHtmlSuffix(route.routes); } }); } getRoutesJSON(opts = {}) { const env = opts.env; return (0, _routesToJSON.default)(this.RoutesManager.routes, this.service, env); } getRouterContent(rendererWrappers) { const config = this.service.config; let defaultRenderer = ` {renderRoutes(routes, props)} `.trim(); if (config.ssr) { // 若有 RendererWrapper ,是使用 {__IS_BROWSER ? : } // 没有时得使用 __IS_BROWSER ? : // 所以使用 Fragment 包一下 defaultRenderer = ` <> {__IS_BROWSER ? {renderRoutes(routes, props)} : renderRoutes(routes, props) } `.trim(); } return rendererWrappers.reduce((memo, wrapper) => { return ` <${wrapper.specifier}> ${memo} `.trim(); }, defaultRenderer); } } exports.default = FilesGenerator;