@@ -3,7 +3,14 @@ import path from 'path'
33import cachedir from 'cachedir'
44import execa from 'execa'
55import { cyTmpDir , projectPath , projects , root } from '../fixtures'
6- import tempDir from 'temp-dir'
6+ import { getYarnCommand } from './yarn'
7+
8+ /**
9+ * Given a package name, returns the path to the module directory on disk.
10+ */
11+ export function pathToPackage ( pkg : string ) : string {
12+ return path . dirname ( require . resolve ( `${ pkg } /package.json` ) )
13+ }
714
815/**
916 * Symlink the cached `node_modules` directory to the temp project directory's `node_modules`.
@@ -26,13 +33,62 @@ async function symlinkNodeModulesFromCache (project: string, cacheDir: string):
2633 console . log ( `📦 node_modules symlink created at ${ from } ` )
2734}
2835
36+ type Dependencies = Record < string , string >
37+
2938/**
30- * Given a package name, returns the path to the module directory on disk .
39+ * Type for package.json files for system-tests example projects .
3140 */
32- function pathToPackage ( pkg : string ) : string {
33- return path . dirname ( require . resolve ( `${ pkg } /package.json` ) )
41+ type SystemTestPkgJson = {
42+ /**
43+ * By default, scaffolding will run `yarn install` if there is a `package.json`.
44+ * This option, if set, disables that.
45+ */
46+ _cySkipDepInstall ?: boolean
47+ /**
48+ * Run the yarn v2-style install command instead of yarn v1-style.
49+ */
50+ _cyYarnV311 ?: boolean
51+ /**
52+ * By default, the automatic `yarn install` will not run postinstall scripts. This
53+ * option, if set, will cause postinstall scripts to run for this project.
54+ */
55+ _cyRunScripts ?: boolean
56+ dependencies ?: Dependencies
57+ devDependencies ?: Dependencies
58+ optionalDependencies ?: Dependencies
3459}
3560
61+ async function getLockFilename ( dir : string ) {
62+ const hasYarnLock = ! ! await fs . stat ( path . join ( dir , 'yarn.lock' ) ) . catch ( ( ) => false )
63+ const hasNpmLock = ! ! await fs . stat ( path . join ( dir , 'package-lock.json' ) ) . catch ( ( ) => false )
64+
65+ if ( hasYarnLock && hasNpmLock ) throw new Error ( `The example project at '${ dir } ' has conflicting lockfiles. Only use one package manager's lockfile per project.` )
66+
67+ if ( hasYarnLock ) return 'yarn.lock'
68+
69+ if ( hasNpmLock ) return 'package-lock.json'
70+ }
71+
72+ function getRelativePathToProjectDir ( projectDir : string ) {
73+ return path . relative ( projectDir , path . join ( root , '..' ) )
74+ }
75+
76+ async function restoreLockFileRelativePaths ( opts : { projectDir : string , lockFilePath : string , relativePathToMonorepoRoot : string } ) {
77+ const relativePathToProjectDir = getRelativePathToProjectDir ( opts . projectDir )
78+ const lockFileContents = ( await fs . readFile ( opts . lockFilePath , 'utf8' ) )
79+ . replaceAll ( opts . relativePathToMonorepoRoot , relativePathToProjectDir )
80+
81+ await fs . writeFile ( opts . lockFilePath , lockFileContents )
82+ }
83+
84+ async function normalizeLockFileRelativePaths ( opts : { project : string , projectDir : string , lockFilePath : string , lockFilename : string , relativePathToMonorepoRoot : string } ) {
85+ const relativePathToProjectDir = getRelativePathToProjectDir ( opts . projectDir )
86+ const lockFileContents = ( await fs . readFile ( opts . lockFilePath , 'utf8' ) )
87+ . replaceAll ( relativePathToProjectDir , opts . relativePathToMonorepoRoot )
88+
89+ // write back to the original project dir, not the tmp copy
90+ await fs . writeFile ( path . join ( projects , opts . project , 'yarn.lock' ) , lockFileContents )
91+ }
3692
3793/**
3894 * Given a path to a `package.json`, convert any references to development
@@ -64,68 +120,10 @@ async function makeWorkspacePackagesAbsolute (pathToPkgJson: string): Promise<st
64120 return updatedDeps
65121}
66122
67- function getYarnCommand ( opts : {
68- yarnV311 : boolean
69- updateYarnLock : boolean
70- isCI : boolean
71- runScripts : boolean
72- } ) : string {
73- let cmd = `yarn install`
74-
75- if ( opts . yarnV311 ) {
76- // @see https://yarnpkg.com/cli/install
77- if ( ! opts . runScripts ) cmd += ' --mode=skip-build'
78-
79- if ( ! opts . updateYarnLock ) cmd += ' --immutable'
80-
81- return cmd
82- }
83-
84- cmd += ' --prefer-offline'
85-
86- if ( ! opts . runScripts ) cmd += ' --ignore-scripts'
87-
88- if ( ! opts . updateYarnLock ) cmd += ' --frozen-lockfile'
89-
90- // yarn v1 has a bug with integrity checking and local cache/dependencies
91- // @see https://github.com/yarnpkg/yarn/issues/6407
92- cmd += ' --update-checksums'
93-
94- // in CircleCI, this offline cache can be used
95- if ( opts . isCI ) cmd += ` --cache-folder=~/.yarn-${ process . platform } `
96- else cmd += ` --cache-folder=${ path . join ( tempDir , 'cy-system-tests-yarn-cache' , String ( Date . now ( ) ) ) } `
97-
98- return cmd
99- }
100-
101- type Dependencies = Record < string , string >
102-
103- /**
104- * Type for package.json files for system-tests example projects.
105- */
106- type SystemTestPkgJson = {
107- /**
108- * By default, scaffolding will run `yarn install` if there is a `package.json`.
109- * This option, if set, disables that.
110- */
111- _cySkipYarnInstall ?: boolean
112- /**
113- * Run the yarn v2-style install command instead of yarn v1-style.
114- */
115- _cyYarnV311 ?: boolean
116- /**
117- * By default, the automatic `yarn install` will not run postinstall scripts. This
118- * option, if set, will cause postinstall scripts to run for this project.
119- */
120- _cyRunScripts ?: boolean
121- dependencies ?: Dependencies
122- devDependencies ?: Dependencies
123- optionalDependencies ?: Dependencies
124- }
125-
126123/**
127124 * Given a `system-tests` project name, detect and install the `node_modules`
128125 * specified in the project's `package.json`. No-op if no `package.json` is found.
126+ * Will use `yarn` or `npm` based on the lockfile present.
129127 */
130128export async function scaffoldProjectNodeModules ( project : string , updateYarnLock : boolean = ! ! process . env . UPDATE_YARN_LOCK ) : Promise < void > {
131129 const projectDir = projectPath ( project )
@@ -156,40 +154,34 @@ export async function scaffoldProjectNodeModules (project: string, updateYarnLoc
156154
157155 console . log ( `📦 Found package.json for project ${ project } .` )
158156
159- if ( pkgJson . _cySkipYarnInstall ) {
160- return console . log ( `📦 cySkipYarnInstall set in package.json, skipping yarn steps` )
157+ if ( pkgJson . _cySkipDepInstall ) {
158+ return console . log ( `📦 _cySkipDepInstall set in package.json, skipping dep-installer steps` )
161159 }
162160
163161 if ( ! pkgJson . dependencies && ! pkgJson . devDependencies && ! pkgJson . optionalDependencies ) {
164- return console . log ( `📦 No dependencies found, skipping yarn steps` )
162+ return console . log ( `📦 No dependencies found, skipping dep-installer steps` )
165163 }
166164
167165 // 1. Ensure there is a cache directory set up for this test project's `node_modules`.
168166 await symlinkNodeModulesFromCache ( project , cacheDir )
169167
170- // 2. Before running `yarn` , resolve workspace deps to absolute paths.
168+ // 2. Before running the package installer , resolve workspace deps to absolute paths.
171169 // This is required to fix `yarn install` for workspace-only packages.
172170 const workspaceDeps = await makeWorkspacePackagesAbsolute ( projectPkgJsonPath )
173171
174172 await removeWorkspacePackages ( workspaceDeps )
175173
176- // 3. Fix relative paths in temp dir's `yarn.lock`.
177- const relativePathToProjectDir = path . relative ( projectDir , path . join ( root , '..' ) )
178- const yarnLockPath = path . join ( projectDir , 'yarn.lock' )
174+ const lockFilename = await getLockFilename ( projectDir )
179175
180- console . log ( '📦 Writing yarn.lock with fixed relative paths to temp dir' )
181- try {
182- const yarnLock = ( await fs . readFile ( yarnLockPath , 'utf8' ) )
183- . replaceAll ( relativePathToMonorepoRoot , relativePathToProjectDir )
176+ if ( ! lockFilename ) throw new Error ( `package.json exists, but missing a lockfile for example project in '${ projectDir } '` )
184177
185- await fs . writeFile ( yarnLockPath , yarnLock )
186- } catch ( err ) {
187- if ( err . code !== 'ENOENT' || ! updateYarnLock ) throw err
178+ // 3. Fix relative paths in temp dir's lockfile.
179+ const lockFilePath = path . join ( projectDir , lockFilename )
188180
189- console . log ( '📦 No yarn.lock found, continuing' )
190- }
181+ console . log ( `📦 Writing ${ lockFilename } with fixed relative paths to temp dir` )
182+ await restoreLockFileRelativePaths ( { projectDir , lockFilePath , relativePathToMonorepoRoot } )
191183
192- // 4. Run `yarn install`.
184+ // 4. Run `yarn/npm install`.
193185 const cmd = getYarnCommand ( {
194186 updateYarnLock,
195187 yarnV311 : pkgJson . _cyYarnV311 ,
@@ -199,17 +191,13 @@ export async function scaffoldProjectNodeModules (project: string, updateYarnLoc
199191
200192 await runCmd ( cmd )
201193
202- console . log ( `📦 Copying yarn.lock and fixing relative paths for ${ project } ` )
203-
204- // Replace workspace dependency paths in `yarn.lock` with tokens so it can be the same
205- // for all developers
206- const yarnLock = ( await fs . readFile ( yarnLockPath , 'utf8' ) )
207- . replaceAll ( relativePathToProjectDir , relativePathToMonorepoRoot )
208-
209- await fs . writeFile ( path . join ( projects , project , 'yarn.lock' ) , yarnLock )
194+ // 5. Now that the lockfile is up to date, update workspace dependency paths in the lockfile with monorepo
195+ // relative paths so it can be the same for all developers
196+ console . log ( `📦 Copying ${ lockFilename } and fixing relative paths for ${ project } ` )
197+ await normalizeLockFileRelativePaths ( { project, projectDir, lockFilePath, lockFilename, relativePathToMonorepoRoot } )
210198
211- // 5 . After `yarn install` , we must now symlink *over* all workspace dependencies, or else
212- // `require` calls from `yarn install`'d workspace deps to peer deps will fail.
199+ // 6 . After install, we must now symlink *over* all workspace dependencies, or else
200+ // `require` calls from installed workspace deps to peer deps will fail.
213201 await removeWorkspacePackages ( workspaceDeps )
214202 for ( const dep of workspaceDeps ) {
215203 console . log ( `📦 Symlinking workspace dependency: ${ dep } ` )
0 commit comments