From fed24d8e491d070b28d6e16ebb326b0cc1531f12 Mon Sep 17 00:00:00 2001 From: Bert De Block Date: Tue, 4 Feb 2025 14:34:26 +0100 Subject: [PATCH] WIP --- src/config.ts | 3 +- src/ember-try.ts | 2 +- src/file-handlers.ts | 22 +++++++ src/files.ts | 29 +++++++- src/index.ts | 6 ++ src/run-scenario.ts | 31 ++++++--- src/types.ts | 3 + test-app/pnpm-lock.yaml | 120 +++++++++++++++++++--------------- test-app/try-or-die.config.js | 88 +++++++++++++------------ 9 files changed, 201 insertions(+), 103 deletions(-) create mode 100644 src/file-handlers.ts create mode 100644 src/index.ts diff --git a/src/config.ts b/src/config.ts index ae29156..de37a8f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -40,9 +40,10 @@ export async function readConfig(cwd: string, _configPath?: string) { ); configFactory = _configFactory; - } catch { + } catch (cause) { throw new PrettyError( `Could not find or import config file \`${configPath}\`.`, + { cause }, ); } diff --git a/src/ember-try.ts b/src/ember-try.ts index 7fc21d0..f510875 100644 --- a/src/ember-try.ts +++ b/src/ember-try.ts @@ -10,7 +10,7 @@ export function isEmberTryScenario(scenario: any) { /** * Transform an `ember-try` scenario to a `try-or-die` scenario. */ -export function transformEmberTryScenario(scenario: any): Scenario { +export function transformEmberTryScenario(scenario: any): Partial { return { env: scenario.env, name: scenario.name, diff --git a/src/file-handlers.ts b/src/file-handlers.ts new file mode 100644 index 0000000..3719fc0 --- /dev/null +++ b/src/file-handlers.ts @@ -0,0 +1,22 @@ +import { flattenObject } from "flatten-anything"; +import set from "lodash.set"; +import { FileHandler } from "./types.js"; + +export function mergeJson(object: any): FileHandler { + return (content) => { + const target = JSON.parse(content); + const objectFlat = flattenObject(object); + + Object.keys(objectFlat).forEach((key) => { + set(target, key, objectFlat[key]); + }); + + return JSON.stringify(target); + }; +} + +export function writeJson(object: any): FileHandler { + return () => { + return JSON.stringify(object); + }; +} diff --git a/src/files.ts b/src/files.ts index 84e410a..8868c5d 100644 --- a/src/files.ts +++ b/src/files.ts @@ -1,4 +1,5 @@ import { createHash } from "node:crypto"; +import { readFile, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { copy, @@ -8,6 +9,32 @@ import { writeJson, } from "fs-extra/esm"; import tempDir from "temp-dir"; +import { PackageJson } from "type-fest"; +import { Scenario } from "./types.js"; + +/** + * Apply a scenario's files. + */ +export function applyScenarioFiles( + cwd: string, + files: Scenario["files"], +): Promise { + return Promise.all( + Object.keys(files).map(async (file) => { + const path = join(cwd, file); + const newContent = files[file]; + + if (typeof newContent === "string") { + await writeFile(path, newContent); + } else if (typeof newContent === "function") { + await writeFile( + path, + await newContent(await readFile(path, { encoding: "utf-8" })), + ); + } + }), + ); +} /** * Back up multiple files. @@ -46,7 +73,7 @@ export function readPackageFile(cwd: string) { /** * Write a `package.json` file. */ -export function writePackageFile(cwd: string, packageJson: object) { +export function writePackageFile(cwd: string, packageJson: PackageJson) { return writeJson(join(cwd, "package.json"), packageJson, { spaces: 2 }); } diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..e627d4c --- /dev/null +++ b/src/index.ts @@ -0,0 +1,6 @@ +import { mergeJson, writeJson } from "./file-handlers.js"; + +export const fileHandlers = { + mergeJson, + writeJson, +}; diff --git a/src/run-scenario.ts b/src/run-scenario.ts index fe03aad..4e3ace2 100644 --- a/src/run-scenario.ts +++ b/src/run-scenario.ts @@ -2,17 +2,24 @@ import { execa } from "execa"; import { flattenObject } from "flatten-anything"; import set from "lodash.set"; import { stderr, stdout } from "node:process"; +import { PackageJson } from "type-fest"; import { debugFactory } from "./debug-factory.js"; import { isEmberTryScenario, transformEmberTryScenario } from "./ember-try.js"; import { applyScenarioEnv } from "./env.js"; import { PrettyError } from "./errors.js"; -import { backUpFiles, readPackageFile, writePackageFile } from "./files.js"; +import { + applyScenarioFiles, + backUpFiles, + readPackageFile, + writePackageFile, +} from "./files.js"; import { PACKAGE_MANAGER_LOCKFILE } from "./package-managers.js"; import { type Command, type Config, type Scenario } from "./types.js"; import { printBanner, printInfo, printSuccess } from "./ui.js"; -const DEFAULT_SCENARIO = { +const DEFAULT_SCENARIO: Scenario = { env: {}, + files: {}, name: "default", packageJson: {}, }; @@ -28,7 +35,7 @@ export async function runScenario( config: Config, { restore = true } = {}, ) { - const scenario = { ...DEFAULT_SCENARIO }; + const scenario: Scenario = { ...DEFAULT_SCENARIO }; const scenarioStartTime = Date.now(); if (isEmberTryScenario(providedScenario)) { @@ -43,18 +50,24 @@ export async function runScenario( printBanner(`START ${scenario.name}`); const lockfile = PACKAGE_MANAGER_LOCKFILE[config.packageManager]; - const packageJson = await readPackageFile(cwd); + // const packageJson = await readPackageFile(cwd); const restoreProcessEnv = applyScenarioEnv(scenario.env); - const restoreFiles = await backUpFiles(cwd, ["package.json", lockfile]); + const restoreFiles = await backUpFiles(cwd, [ + // "package.json", + lockfile, + ...Object.keys(scenario.files), + ]); + const restoreState = async () => { restoreProcessEnv(); await restoreFiles(); }; - applyScenarioPackageJson(packageJson, scenario.packageJson); + // applyScenarioPackageJson(packageJson, scenario.packageJson); - await writePackageFile(cwd, packageJson); + await applyScenarioFiles(cwd, scenario.files); + // await writePackageFile(cwd, packageJson); await runCommand({ command: [ config.packageManager, @@ -94,8 +107,8 @@ export async function runScenario( } function applyScenarioPackageJson( - packageJson: object, - packageJsonScenario: object, + packageJson: PackageJson, + packageJsonScenario: PackageJson, ) { const packageJsonScenarioFlat = flattenObject(packageJsonScenario); diff --git a/src/types.ts b/src/types.ts index ec8757e..cf86d37 100644 --- a/src/types.ts +++ b/src/types.ts @@ -15,8 +15,11 @@ export interface PrintOptions { prefix?: string; } +export type FileHandler = (content: string) => string | Promise; + export interface Scenario { env: Record; + files: { [filePath: string]: string | FileHandler }; name: string; npm?: Record; packageJson: PackageJson; diff --git a/test-app/pnpm-lock.yaml b/test-app/pnpm-lock.yaml index dd55bb1..3c9f278 100644 --- a/test-app/pnpm-lock.yaml +++ b/test-app/pnpm-lock.yaml @@ -1,20 +1,23 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false -devDependencies: - '@embroider/test-setup': - specifier: ^3.0.1 - version: 3.0.1 - ember-source-channel-url: - specifier: ^3.0.0 - version: 3.0.0 +importers: + + .: + devDependencies: + '@embroider/test-setup': + specifier: ^3.0.1 + version: 3.0.1 + ember-source-channel-url: + specifier: ^3.0.0 + version: 3.0.0 packages: - /@embroider/test-setup@3.0.1: + '@embroider/test-setup@3.0.1': resolution: {integrity: sha512-t7R2f10iZDSNw3ovWAPM63GRQTu63uihpVh6CvHev5XkLt8B7tdxcxGyO6RbdF5Hu+pbsUu8sD0kAlR1gopmxg==} engines: {node: 12.* || 14.* || >= 16} peerDependencies: @@ -28,43 +31,26 @@ packages: optional: true '@embroider/webpack': optional: true - dependencies: - lodash: 4.17.21 - resolve: 1.22.4 - dev: true - /ember-source-channel-url@3.0.0: + ember-source-channel-url@3.0.0: resolution: {integrity: sha512-vF/8BraOc66ZxIDo3VuNP7iiDrnXEINclJgSJmqwAAEpg84Zb1DHPI22XTXSDA+E8fW5btPUxu65c3ZXi8AQFA==} engines: {node: 10.* || 12.* || >= 14} hasBin: true - dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - dev: true - /function-bind@1.1.1: + function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - dev: true - /has@1.0.3: + has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 - dev: true - /is-core-module@2.13.0: + is-core-module@2.13.0: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} - dependencies: - has: 1.0.3 - dev: true - /lodash@4.17.21: + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true - /node-fetch@2.7.0: + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} peerDependencies: @@ -72,39 +58,71 @@ packages: peerDependenciesMeta: encoding: optional: true - dependencies: - whatwg-url: 5.0.0 - dev: true - /path-parse@1.0.7: + path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true - /resolve@1.22.4: + resolve@1.22.4: resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} hasBin: true - dependencies: - is-core-module: 2.13.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: true - /supports-preserve-symlinks-flag@1.0.0: + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - dev: true - /tr46@0.0.3: + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true - /webidl-conversions@3.0.1: + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true - /whatwg-url@5.0.0: + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + +snapshots: + + '@embroider/test-setup@3.0.1': + dependencies: + lodash: 4.17.21 + resolve: 1.22.4 + + ember-source-channel-url@3.0.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + function-bind@1.1.1: {} + + has@1.0.3: + dependencies: + function-bind: 1.1.1 + + is-core-module@2.13.0: + dependencies: + has: 1.0.3 + + lodash@4.17.21: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + path-parse@1.0.7: {} + + resolve@1.22.4: + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tr46@0.0.3: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: true diff --git a/test-app/try-or-die.config.js b/test-app/try-or-die.config.js index 6d1b3c1..ec58a2c 100644 --- a/test-app/try-or-die.config.js +++ b/test-app/try-or-die.config.js @@ -1,7 +1,8 @@ /* eslint-disable n/no-missing-import */ -import { embroiderOptimized, embroiderSafe } from "@embroider/test-setup"; -import emberSourceChannelURL from "ember-source-channel-url"; +// import { embroiderOptimized, embroiderSafe } from "@embroider/test-setup"; +// import emberSourceChannelURL from "ember-source-channel-url"; +import { fileHandlers } from "../dist/index.js"; /** @type {import("../src/types").Config} */ export default { @@ -10,45 +11,52 @@ export default { scenarios: [ { name: "ember-lts-4.8", - packageJson: { - devDependencies: { - "ember-source": "~4.8.0", - }, + files: { + "package.json": fileHandlers.mergeJson({ + devDependencies: { + "ember-source": "~4.8.0", + }, + }), }, + // packageJson: { + // devDependencies: { + // "ember-source": "~4.8.0", + // }, + // }, }, - { - name: "ember-lts-4.12", - packageJson: { - devDependencies: { - "ember-source": "~4.12.0", - }, - }, - }, - { - name: "ember-release", - packageJson: { - devDependencies: { - "ember-source": await emberSourceChannelURL("release"), - }, - }, - }, - { - name: "ember-beta", - packageJson: { - devDependencies: { - "ember-source": await emberSourceChannelURL("beta"), - }, - }, - }, - { - name: "ember-canary", - packageJson: { - devDependencies: { - "ember-source": await emberSourceChannelURL("canary"), - }, - }, - }, - embroiderSafe(), - embroiderOptimized(), + // { + // name: "ember-lts-4.12", + // packageJson: { + // devDependencies: { + // "ember-source": "~4.12.0", + // }, + // }, + // }, + // { + // name: "ember-release", + // packageJson: { + // devDependencies: { + // "ember-source": await emberSourceChannelURL("release"), + // }, + // }, + // }, + // { + // name: "ember-beta", + // packageJson: { + // devDependencies: { + // "ember-source": await emberSourceChannelURL("beta"), + // }, + // }, + // }, + // { + // name: "ember-canary", + // packageJson: { + // devDependencies: { + // "ember-source": await emberSourceChannelURL("canary"), + // }, + // }, + // }, + // embroiderSafe(), + // embroiderOptimized(), ], };