Skip to content

chore: configurable force deferred in snapshot doctor #31865

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: release/15.0.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,13 @@
"volar.takeOverMode.enabled": "auto",

"editor.tabSize": 2,
"coverage-gutters.coverageFileNames": [
"coverage-final.json",
"lcov.info",
"cov.xml",
"coverage.xml",
"cobertura.xml",
"jacoco.xml",
"coverage.cobertura.xml"
],
}
4 changes: 3 additions & 1 deletion .yarnclean
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ wercker.yml
.flowconfig
.documentup.json
.yarn-metadata.json
.travis.yml
.travis.yml

!node_modules/istanbul-reports/lib/html/assets
1 change: 1 addition & 0 deletions packages/data-context/src/util/config-file-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export async function insertValueInJSString (fileContents: string, obj: Record<s
&& nodePath.node.callee.type === 'Identifier') {
const functionName = nodePath.node.callee.name

// @ts-expect-error - ast is the wrong type
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is one of those random ts errors that occasionally crop up up that prevents build, even if the file isn't changed.

if (isDefineConfigFunction(ast, functionName)) {
return handleExport(nodePath.get('arguments', 0))
}
Expand Down
8 changes: 6 additions & 2 deletions tooling/v8-snapshot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"test": "yarn test-unit",
"test-integration": "mocha test/integration/**/*.spec.ts --config ./test/.mocharc.js",
"test-unit": "mocha test/unit/**/*.spec.ts --config ./test/.mocharc.js",
"watch": "tsc --watch"
"test-vspec": "vitest run",
"watch": "tsc --watch",
"watch-vspec": "vitest watch"
},
"dependencies": {
"@tooling/electron-mksnapshot": "0.0.0-development",
Expand All @@ -29,11 +31,13 @@
},
"devDependencies": {
"@tooling/system-tests": "0.0.0-development",
"@vitest/coverage-v8": "2.1.8",
"chai-as-promised": "7.1.1",
"cpr": "^3.0.1",
"mocha": "7.0.1",
"snap-shot-it": "7.9.10",
"stealthy-require": "^1.1.1"
"stealthy-require": "^1.1.1",
"vitest": "2.1.8"
},
"files": [
"dist",
Expand Down
13 changes: 13 additions & 0 deletions tooling/v8-snapshot/src/doctor/dependency-match.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Checks if a dependency matches a force no rewrite entry
* @param dependency - The dependency to check
* @param forceNorewrite - The force no rewrite entry
* @returns true if the dependency matches the force no rewrite entry, false otherwise
*/
export const doesDependencyMatchForceNorewriteEntry = (dependency: string, forceNorewrite: string) => {
// The force no rewrite file follows a convention where we try
// and match all possible node_modules paths if the force no
// rewrite entry starts with "*/". If it does not
// start with "*" then it is an exact match.
return (forceNorewrite.startsWith('*/') && dependency.endsWith(forceNorewrite.slice(2))) || dependency === forceNorewrite
}
35 changes: 27 additions & 8 deletions tooling/v8-snapshot/src/doctor/determine-deferred.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import debug from 'debug'
import fs from 'fs'
import path from 'path'
import { doesDependencyMatchForceNorewriteEntry, SnapshotDoctor } from './snapshot-doctor'
import { SnapshotDoctor } from './snapshot-doctor'
import { canAccess, createHashForFile, matchFileHash } from '../utils'

import { doesDependencyMatchForceNorewriteEntry } from './dependency-match'
import { forceDeferred } from '../setup/force-deferred'
const logInfo = debug('cypress:snapgen:info')

interface ErrorOnInvalidForceNorewriteOpts {
Expand Down Expand Up @@ -49,6 +50,20 @@ function errorOnInvalidForceNorewrite (opts: ErrorOnInvalidForceNorewriteOpts) {
}
}

export interface SnapshotMetadata {
deferredHash: string
norewrite: string[]
deferred: string[]
healthy: string[]
}

async function importCachedSnapshotMetadata (cacheDir: string): Promise<SnapshotMetadata> {
const jsonPath = path.join(cacheDir, 'snapshot-meta.json')
const contents = await fs.promises.readFile(jsonPath, 'utf8')

return JSON.parse(contents) satisfies SnapshotMetadata
}

export async function determineDeferred (
bundlerPath: string,
projectBaseDir: string,
Expand All @@ -63,11 +78,12 @@ export async function determineDeferred (
},
) {
const jsonPath = path.join(cacheDir, 'snapshot-meta.json')

const usePreviousSnapshotMetadata = (!process.env.V8_SNAPSHOT_FROM_SCRATCH || !['1', 'true'].includes(process.env.V8_SNAPSHOT_FROM_SCRATCH)) && await canAccess(jsonPath)
const { deferredHash, norewrite, deferred, healthy } = usePreviousSnapshotMetadata ? require(jsonPath) : { deferredHash: '', norewrite: [], deferred: [], healthy: [] }
const { deferredHash, norewrite, deferred, healthy } = usePreviousSnapshotMetadata ? await importCachedSnapshotMetadata(cacheDir) : await Promise.resolve({ deferredHash: '', norewrite: [], deferred: [], healthy: [] })

const hashFilePath = await findHashFile(projectBaseDir)
const currentHash = await createHashForFile(hashFilePath)
const res = await matchFileHash(hashFilePath, deferredHash)

let nodeModulesHealthy: string[] = []
let projectHealthy: string[] = []
Expand Down Expand Up @@ -105,7 +121,10 @@ export async function determineDeferred (
}
})

if (res.match && opts.nodeModulesOnly) {
const res = await matchFileHash(hashFilePath, deferredHash)
const fileHashMatchesDeferredHash = res.match

if (fileHashMatchesDeferredHash && opts.nodeModulesOnly) {
const combined: Set<string> = new Set([
...currentNorewrite,
...opts.forceNorewrite,
Expand All @@ -127,9 +146,9 @@ export async function determineDeferred (
entryFilePath: snapshotEntryFile,
baseDirPath: projectBaseDir,
nodeModulesOnly: opts.nodeModulesOnly,
previousDeferred: currentDeferred,
previousHealthy: currentHealthy,
previousNorewrite: currentNorewrite,
previousDeferred: new Set([...currentDeferred, ...forceDeferred()]),
previousHealthy: new Set(currentHealthy),
previousNorewrite: new Set(currentNorewrite),
forceNorewrite: opts.forceNorewrite,
nodeEnv: opts.nodeEnv,
cypressInternalEnv: opts.cypressInternalEnv,
Expand Down
15 changes: 1 addition & 14 deletions tooling/v8-snapshot/src/doctor/snapshot-doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
WarningConsequence,
WarningsProcessor,
} from './warnings-processor'
import { doesDependencyMatchForceNorewriteEntry } from './dependency-match'

const logInfo = debug('cypress:snapgen:info')
const logDebug = debug('cypress:snapgen:debug')
Expand Down Expand Up @@ -48,20 +49,6 @@ export type SnapshotDoctorOpts = Omit<
forceNorewrite: Set<string>
}

/**
* Checks if a dependency matches a force no rewrite entry
* @param dependency - The dependency to check
* @param forceNorewrite - The force no rewrite entry
* @returns true if the dependency matches the force no rewrite entry, false otherwise
*/
export const doesDependencyMatchForceNorewriteEntry = (dependency: string, forceNorewrite: string) => {
// The force no rewrite file follows a convention where we try
// and match all possible node_modules paths if the force no
// rewrite entry starts with "*/". If it does not
// start with "*" then it is an exact match.
return (forceNorewrite.startsWith('*/') && dependency.endsWith(forceNorewrite.slice(2))) || dependency === forceNorewrite
}

/**
* Tracks which modules have been deferred, need to be deferred and so on
* during the doctor process
Expand Down
10 changes: 4 additions & 6 deletions tooling/v8-snapshot/src/generator/snapshot-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,12 @@ export class SnapshotGenerator {
if (this.useExistingSnapshotScript) {
let contents = await fs.promises.readFile(this.snapshotScriptPath, 'utf8')

console.log('??', contents, this.updateSnapshotScriptContents)
if (this.updateSnapshotScriptContents) {
contents = this.updateSnapshotScriptContents(contents)
}

console.log('??', contents)
this.snapshotScript = Buffer.from(contents)
await fs.promises.writeFile(this.snapshotScriptPath, this.snapshotScript)

Expand Down Expand Up @@ -339,7 +341,8 @@ export class SnapshotGenerator {
// errors we verify that the generated script is snapshot-able.
logInfo('Verifying snapshot script')
try {
this._verifyScript()
assert(this.snapshotScript != null, 'need snapshotScript to be set')
this._snapshotVerifier.verify(this.snapshotScript, this.snapshotScriptPath)
} catch (err) {
logInfo(`Script failed verification, writing to ${this.snapshotScriptPath}`)

Expand Down Expand Up @@ -604,9 +607,4 @@ export class SnapshotGenerator {

throw new Error('make snapshot failed')
}

private _verifyScript () {
assert(this.snapshotScript != null, 'need snapshotScript to be set')
this._snapshotVerifier.verify(this.snapshotScript, this.snapshotScriptPath)
}
}
3 changes: 3 additions & 0 deletions tooling/v8-snapshot/src/setup/force-deferred.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function forceDeferred (): string[] {
return []
}
6 changes: 6 additions & 0 deletions tooling/v8-snapshot/test/fixtures/force-deferred.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"empty": [],
"withValues": [
"node_modules/some-package/src/index.js"
]
}
Loading