From efaaa97986dc8a03fc9f68e3da45afcc3bb10177 Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Sun, 8 Dec 2024 01:41:32 +0000 Subject: [PATCH] test(transformer): update fixtures script support overrides (#7688) Support overriding test fixtures in `update_fixtures.js` script. Any files in `tasks/transform_conformance/overrides` are copied into Babel test fixtures. If `options.json` is overridden, then script runs Babel with updated options, to generate a new `output` file for the fixture. --- .../transform_conformance/update_fixtures.js | 99 ++++++++++++++----- 1 file changed, 77 insertions(+), 22 deletions(-) diff --git a/tasks/transform_conformance/update_fixtures.js b/tasks/transform_conformance/update_fixtures.js index 8406d59dac103..3d5ce11fd1421 100644 --- a/tasks/transform_conformance/update_fixtures.js +++ b/tasks/transform_conformance/update_fixtures.js @@ -13,8 +13,9 @@ // (or maybe don't - what does this option mean?) import { transformFileAsync } from '@babel/core'; -import { readdir, readFile, rename, writeFile } from 'fs/promises'; -import { join as pathJoin } from 'path'; +import assert from 'assert'; +import { copyFile, readdir, readFile, rename, writeFile } from 'fs/promises'; +import { extname, join as pathJoin } from 'path'; const PACKAGES = ['babel-plugin-transform-class-properties']; const FILTER_OUT_PRESETS = ['env']; @@ -25,6 +26,7 @@ const FILTER_OUT_PLUGINS = [ ]; const PACKAGES_PATH = pathJoin(import.meta.dirname, '../coverage/babel/packages'); +const OVERRIDES_PATH = pathJoin(import.meta.dirname, 'overrides'); // Copied from `@babel/helper-transform-fixture-test-runner` const EXTERNAL_HELPERS_VERSION = '7.100.0'; @@ -45,41 +47,83 @@ async function updateDir(dirPath, options, hasChangedOptions) { const files = await readdir(dirPath, { withFileTypes: true }); const dirFiles = []; - let optionsFile, inputFile, outputFile; + + const filenames = { options: null, input: null, output: null, exec: null }; + const overrides = { options: false, input: false, output: false, exec: false }; + + // Find files in dir for (const file of files) { + const filename = file.name; if (file.isDirectory()) { - dirFiles.push(file); - } else if (file.name === 'options.json') { - optionsFile = file; - } else if (file.name.startsWith('output.')) { - outputFile = file; - } else if (file.name.startsWith('input.')) { - inputFile = file; + dirFiles.push(filename); + } else { + const ext = extname(filename), + type = ext === '' ? filename : filename.slice(0, -ext.length); + if (Object.hasOwn(filenames, type)) filenames[type] = filename; + } + } + + // Find override files + const overridesDirPath = pathJoin(`${OVERRIDES_PATH}${dirPath.slice(PACKAGES_PATH.length)}`); + let overrideFiles; + try { + overrideFiles = await readdir(overridesDirPath, { withFileTypes: true }); + } catch (err) { + if (err?.code !== 'ENOENT') throw err; + } + + if (overrideFiles) { + for (const file of overrideFiles) { + if (file.isDirectory()) continue; + + const filename = file.name; + // `reason.txt` files are to document why override is used + if (filename === 'reason.txt') continue; + + const ext = extname(filename), + type = filename.slice(0, -ext.length), + path = pathJoin(overridesDirPath, filename); + + assert(Object.hasOwn(overrides, type), `Unexpected override file: ${path}`); + + const originalPath = pathJoin(dirPath, filename); + if (filenames[type]) { + const originalFilename = filenames[type]; + assert(originalFilename === filename, `Unmatched override file: ${path} (original: ${originalFilename})`); + await backupFile(originalPath); + } + + filenames[type] = filename; + overrides[type] = true; + if (type === 'options') hasChangedOptions = true; + + await copyFile(path, originalPath); } } - if (optionsFile) { - const path = pathJoin(dirPath, optionsFile.name); + // Update options, save to file, and merge options with parent + if (filenames.options) { + const path = pathJoin(dirPath, filenames.options); const localOptions = JSON.parse(await readFile(path, 'utf8')); - if (updateOptions(localOptions)) { + if (!overrides.options && updateOptions(localOptions)) { hasChangedOptions = true; - const backupPath = pathJoin(dirPath, 'options.original.json'); - await rename(path, backupPath); + await backupFile(path); await writeFile(path, JSON.stringify(localOptions, null, 2) + '\n'); } options = mergeOptions(options, localOptions); } - if (outputFile && hasChangedOptions) { - const inputPath = pathJoin(dirPath, inputFile.name); - const outputPath = pathJoin(dirPath, outputFile.name); - const backupOutputPath = pathJoin(dirPath, `output.original.${outputFile.name.slice(7)}`); - await rename(outputPath, backupOutputPath); + // Run Babel with updated options/input + if (filenames.output && (hasChangedOptions || overrides.input) && !overrides.output) { + const inputPath = pathJoin(dirPath, filenames.input), + outputPath = pathJoin(dirPath, filenames.output); + await backupFile(outputPath); await transform(inputPath, outputPath, options); } - for (const file of dirFiles) { - const path = pathJoin(dirPath, file.name); + // Process subfolders + for (const filename of dirFiles) { + const path = pathJoin(dirPath, filename); await updateDir(path, options, hasChangedOptions); } } @@ -211,3 +255,14 @@ function getName(stringOrArray) { if (Array.isArray(stringOrArray)) return stringOrArray[0]; return stringOrArray; } + +/** + * Backup file. + * @param {string} path - Original path + * @returns {undefined} + */ +async function backupFile(path) { + const ext = extname(path), + backupPath = `${path.slice(0, -ext.length)}.original${ext}`; + await rename(path, backupPath); +}