/
githubmirror
/
amphtml
Обзор
Документация
Войти
/
githubmirror
/
amphtml
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
build-system/tasks/check-sourcemaps.js
191 строка
7 KB
Daniel Rozenberg
🏗 Partial updates for core devDependencies (#39768)
26 янв 2024, 23:04
Не верифицирован
26 янв 2024, 23:04
3dcace4
Код
Авторство
О чём код?
'use strict'; const argv = require('minimist')(process.argv.slice(2)); const fs = require('fs'); const {cyan, green, red} = require('kleur/colors'); const {decode} = require('@jridgewell/sourcemap-codec'); const {execOrDie} = require('../common/exec'); const {log} = require('../common/logging'); // Compile related constants const distWithSourcemapsCmd = 'amp dist --core_runtime_only --extensions=amp-audio --full_sourcemaps'; const distEsmWithSourcemapsCmd = 'amp dist --core_runtime_only --extensions=amp-audio --full_sourcemaps --esm'; // Sourcemap URL related constants const sourcemapUrlMatcher = 'https://raw.githubusercontent.com/ampproject/amphtml/\\d{13}/'; /** * Build runtime with sourcemaps if needed. */ function maybeBuild() { if (!argv.nobuild) { log('Compiling', cyan('v0.js'), 'with full sourcemaps...'); execOrDie(distWithSourcemapsCmd, {'stdio': 'ignore'}); log('Compiling', cyan('v0.mjs'), 'with full sourcemaps...'); execOrDie(distEsmWithSourcemapsCmd, {'stdio': 'ignore'}); } } /** * Verifies that the sourcemap file exists, and returns its contents. * @param {string} map The map filepath to check * @return {!Object} */ function getSourcemapJson(map) { if (!fs.existsSync(map)) { log(red('ERROR:'), 'Could not find', cyan(map)); throw new Error(`Could not find sourcemap file '${map}'`); } return JSON.parse(fs.readFileSync(map, 'utf8')); } /** * Verifies that a correctly formatted sourcemap URL is present in v0.js.map. * * @param {!Object} sourcemapJson * @param {string} map The map filepath to check */ function checkSourcemapUrl(sourcemapJson, map) { log('Inspecting', cyan('sourceRoot'), 'in', cyan(map) + '...'); if (!sourcemapJson.sourceRoot) { log(red('ERROR:'), 'Could not find', cyan('sourceRoot')); throw new Error('Could not find sourcemap URL'); } if (!sourcemapJson.sourceRoot.match(sourcemapUrlMatcher)) { log(red('ERROR:'), cyan(sourcemapJson.sourceRoot), 'is badly formatted'); throw new Error('Badly formatted sourcemap URL'); } } /** * Verifies all the paths in the sources field are as expected. * * @param {!Object} sourcemapJson * @param {string} map The map filepath to check */ function checkSourcemapSources(sourcemapJson, map) { log('Inspecting', cyan('sources'), 'in', cyan(map) + '...'); if (!sourcemapJson.sources) { log(red('ERROR:'), 'Could not find', cyan('sources')); throw new Error('Could not find sources array'); } /** @type {string[]} */ const invalidSources = sourcemapJson.sources .filter((source) => !source.match(/\[.*\]/)) // Ignore non-path sources '[...]' .filter((source) => !fs.existsSync(source)); // All source paths should exist if (invalidSources.length > 0) { log( red('ERROR:'), 'Found invalid paths in', cyan('sources') + ':', cyan(invalidSources.join(', ')) ); throw new Error('Invalid paths in sources array'); } } /** * Performs a sanity check on the mappings field in the sourcemap file. * * Today, the first line of amp.js after resolving imports comes from * src/polyfills/array-includes.js. (The import chain is src/amp.js -> * src/polyfills/index.js -> src/polyfills/array-includes.js.) This sequence * changes rarely, so we can use it as a sentinel value. Here is the process: * * 1. Decode the 'mappings' field into a 3d array using 'sourcemap-codec'. * 2. Extract the mapping for the first line of code in minified v0.js. * 3. Compute the name of the source file that corresponds to this line. * 4. Read the source file and extract the corresponding line of code. * 5. Check if the filename, line of code, and column match expected sentinel values. * * @param {!Object} sourcemapJson * @param {string} map The map filepath to check */ function checkSourcemapMappings(sourcemapJson, map) { log('Inspecting', cyan('mappings'), 'in', cyan(map) + '...'); if (!sourcemapJson.mappings) { log(red('ERROR:'), 'Could not find', cyan('mappings')); throw new Error('Could not find mappings array'); } // See https://www.npmjs.com/package/sourcemap-codec#usage. const firstLineMapping = decode(sourcemapJson.mappings)[1][0]; const [, sourceIndex = 0, sourceCodeLine = 0, sourceCodeColumn] = firstLineMapping; const firstLineFile = sourcemapJson.sources[sourceIndex]; const contents = fs.readFileSync(firstLineFile, 'utf8').split('\n'); const firstLineCode = contents[sourceCodeLine].slice(sourceCodeColumn); const helpMessage = 'If this change is intentional, update the mapping related constants in ' + cyan('build-system/tasks/check-sourcemaps.js') + '.'; // Mapping related constants const expectedFirstLine = map.includes('mjs') ? { file: 'src/polyfills/abort-controller.js', code: 'class AbortController {', } : { file: 'node_modules/@babel/runtime/helpers/esm/createClass.js', code: 'function _defineProperties(target, props) {', }; if (firstLineFile != expectedFirstLine.file) { log(red('ERROR:'), 'Found mapping for incorrect file.'); log('Actual:', cyan(firstLineFile)); log('Expected:', cyan(expectedFirstLine.file)); log(helpMessage); throw new Error('Found mapping for incorrect file'); } if (firstLineCode != expectedFirstLine.code) { log(red('ERROR:'), 'Found mapping for incorrect code.'); log('Actual:', cyan(firstLineCode)); log('Expected:', cyan(expectedFirstLine.code)); log(helpMessage); throw new Error('Found mapping for incorrect code'); } } /** * @param {string} map The map filepath to check * @param {boolean} checkMappings Whether to test a mapping points to a correct source. */ function checkSourceMap(map, checkMappings) { const sourcemapJson = getSourcemapJson(map); checkSourcemapUrl(sourcemapJson, map); checkSourcemapSources(sourcemapJson, map); if (checkMappings) { checkSourcemapMappings(sourcemapJson, map); } } /** * Checks sourcemaps generated during minified compilation for correctness. * Entry point for `amp check-sourcemaps`. * @return {Promise<void>} */ async function checkSourcemaps() { maybeBuild(); checkSourceMap('dist/v0.js.map', true); checkSourceMap('dist/v0.mjs.map', true); checkSourceMap('dist/v0/amp-audio-0.1.js.map', false); checkSourceMap('dist/v0/amp-audio-0.1.mjs.map', false); log(green('SUCCESS:'), 'All sourcemaps checks passed.'); } module.exports = { checkSourcemaps, }; checkSourcemaps.description = 'Check sourcemaps generated during minified compilation for correctness'; checkSourcemaps.flags = { 'nobuild': 'Skip building the runtime (checks previously built code)', };