UNPKG

snowpack

Version:

The ESM-powered frontend build tool. Fast, lightweight, unbundled.

247 lines (246 loc) 10.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildFile = exports.runPipelineCleanupStep = exports.runPipelineOptimizeStep = void 0; const path_1 = __importDefault(require("path")); const source_map_1 = require("source-map"); const url_1 = __importDefault(require("url")); const config_1 = require("../config"); const logger_1 = require("../logger"); const util_1 = require("../util"); const import_css_1 = require("./import-css"); /** * Build Plugin First Pass: If a plugin defines a * `resolve` object, check it against the current * file's extension. If it matches, call the load() * functon and return it's result. * * If no match is found, fall back to just reading * the file from disk and return it. */ async function runPipelineLoadStep(srcPath, { isDev, isSSR, isPackage, isHmrEnabled, config }) { const srcExt = util_1.getExtension(srcPath); const result = {}; for (const step of config.plugins) { if (!step.resolve || !step.resolve.input.some((ext) => srcPath.endsWith(ext))) { continue; } if (!step.load) { continue; } // v3.1: Some plugins break when run on node_modules/. This fix is in place until the plugins themselves have a chance to update. if (step.name.endsWith('@prefresh/snowpack/dist/index.js') && isPackage) { continue; } try { const debugPath = path_1.default.relative(config.root, srcPath); logger_1.logger.debug(`load() starting… [${debugPath}]`, { name: step.name }); const stepResult = await step.load({ fileExt: srcExt, filePath: srcPath, isDev, isSSR, isPackage, isHmrEnabled, }); logger_1.logger.debug(`✔ load() success [${debugPath}]`, { name: step.name }); config_1.validatePluginLoadResult(step, stepResult); if (typeof stepResult === 'string' || Buffer.isBuffer(stepResult)) { const mainOutputExt = step.resolve.output[0]; result[mainOutputExt] = { code: stepResult }; } else if (stepResult && typeof stepResult === 'object') { Object.keys(stepResult).forEach((ext) => { const output = stepResult[ext]; // normalize to {code, map} format if (typeof output === 'string' || Buffer.isBuffer(output)) { result[ext] = { code: output }; } else if (output) { result[ext] = output; } // ensure source maps are strings (it’s easy for plugins to pass back a JSON object) if (result[ext].map && typeof result[ext].map === 'object') { result[ext].map = JSON.stringify(stepResult[ext].map); } // if source maps disabled, don’t return any if (!config.buildOptions.sourcemap) result[ext].map = undefined; }); break; } } catch (err) { // Attach metadata detailing where the error occurred. err.__snowpackBuildDetails = { name: step.name, step: 'load' }; throw err; } } // handle CSS Modules, after plugins run if (import_css_1.needsCSSModules(srcPath)) { let contents = result['.css'] ? result['.css'].code : (await util_1.readFile(srcPath)); if (contents) { // for CSS Modules URLs, we only need the destination URL (POSIX-style) let url = srcPath; for (const dir in config.mount) { if (srcPath.startsWith(dir)) { url = srcPath .replace(dir, config.mount[dir].url) .replace(/\\/g, '/') .replace(/\/+/g, '/') .replace(/\.(scss|sass)$/i, '.css'); break; } } const { css, json } = await import_css_1.cssModules({ contents, url }); result['.css'] = { ...(result['.css'] || {}), code: css }; result['.json'] = { code: JSON.stringify(json) }; } } // if no result was generated, return file as-is if (!Object.keys(result).length) { return { [srcExt]: { code: await util_1.readFile(srcPath), }, }; } return result; } async function composeSourceMaps(id, base, derived) { const [baseMap, transformedMap] = await Promise.all([ new source_map_1.SourceMapConsumer(base), new source_map_1.SourceMapConsumer(derived), ]); try { const generator = source_map_1.SourceMapGenerator.fromSourceMap(transformedMap); generator.applySourceMap(baseMap, id); return generator.toString(); } finally { baseMap.destroy(); transformedMap.destroy(); } } /** * Build Plugin Second Pass: If a plugin defines a * transform() method,call it. Transform cannot change * the file extension, and was designed to run on * every file type and return null/undefined if no * change needed. */ async function runPipelineTransformStep(output, srcPath, { isDev, isHmrEnabled, isPackage, isSSR, config }) { const rootFilePath = util_1.removeExtension(srcPath, util_1.getExtension(srcPath)); const rootFileName = path_1.default.basename(rootFilePath); for (const step of config.plugins) { if (!step.transform) { continue; } // v3.1: Some plugins break when run on node_modules/. This fix is in place until the plugins themselves have a chance to update. if (step.name.endsWith('@prefresh/snowpack/dist/index.js') && isPackage) { continue; } try { for (const destExt of Object.keys(output)) { const destBuildFile = output[destExt]; const { code } = destBuildFile; const fileName = rootFileName + destExt; const filePath = rootFilePath + destExt; const debugPath = path_1.default.relative(config.root, filePath); logger_1.logger.debug(`transform() starting… [${debugPath}]`, { name: step.name }); const result = await step.transform({ contents: code, isDev, isPackage, fileExt: destExt, id: filePath, srcPath, // @ts-ignore: Deprecated filePath: fileName, // @ts-ignore: Deprecated urlPath: `./${path_1.default.basename(rootFileName + destExt)}`, isHmrEnabled, isSSR, }); logger_1.logger.debug(`✔ transform() success [${debugPath}]`, { name: step.name }); if (typeof result === 'string' || Buffer.isBuffer(result)) { // V2 API, simple string variant output[destExt].code = result; output[destExt].map = undefined; } else if (result && typeof result === 'object') { // V2 API, structured result variant const contents = result.contents || result.result; if (contents) { output[destExt].code = contents; const map = result.map; let outputMap = undefined; if (map && config.buildOptions.sourcemap) { // if source maps disabled, don’t return any if (output[destExt].map) { outputMap = await composeSourceMaps(filePath, output[destExt].map, map); } else { outputMap = typeof map === 'object' ? JSON.stringify(map) : map; } } output[destExt].map = outputMap; } } } } catch (err) { // Attach metadata detailing where the error occurred. err.__snowpackBuildDetails = { name: step.name, step: 'transform' }; throw err; } } return output; } async function runPipelineOptimizeStep(buildDirectory, { config }) { for (const step of config.plugins) { if (!step.optimize) { continue; } try { logger_1.logger.debug('optimize() starting…', { name: step.name }); await step.optimize({ buildDirectory, // @ts-ignore: internal API only log: (msg) => { logger_1.logger.info(msg, { name: step.name }); }, }); logger_1.logger.debug('✔ optimize() success', { name: step.name }); } catch (err) { logger_1.logger.error(err.toString() || err, { name: step.name }); process.exit(1); // exit on error } } return null; } exports.runPipelineOptimizeStep = runPipelineOptimizeStep; async function runPipelineCleanupStep({ plugins }) { for (const step of plugins) { if (!step.cleanup) { continue; } await step.cleanup(); } } exports.runPipelineCleanupStep = runPipelineCleanupStep; /** Core Snowpack file pipeline builder */ async function buildFile(srcURL, buildFileOptions) { // Pass 1: Find the first plugin to load this file, and return the result const loadResult = await runPipelineLoadStep(url_1.default.fileURLToPath(srcURL), buildFileOptions); // Pass 2: Pass that result through every plugin transform() method. const transformResult = await runPipelineTransformStep(loadResult, url_1.default.fileURLToPath(srcURL), buildFileOptions); // Return the final build result. return transformResult; } exports.buildFile = buildFile;