From ec5aeea0e8933032204bd0e1317cdb25ea8784b5 Mon Sep 17 00:00:00 2001 From: Chang-Hung Liang Date: Tue, 30 Jan 2024 20:59:00 +0800 Subject: [PATCH 01/12] fix(cli): `zapier build` fails with npm workspaces --- packages/cli/src/utils/build.js | 32 +++++++++++---- packages/cli/src/utils/files.js | 73 +++++++++++++++++---------------- packages/cli/src/utils/misc.js | 49 ++++++++++++++-------- 3 files changed, 96 insertions(+), 58 deletions(-) diff --git a/packages/cli/src/utils/build.js b/packages/cli/src/utils/build.js index 08e7adacf..ebc92e180 100644 --- a/packages/cli/src/utils/build.js +++ b/packages/cli/src/utils/build.js @@ -45,7 +45,7 @@ const { const checkMissingAppInfo = require('./check-missing-app-info'); -const { runCommand, isWindows } = require('./misc'); +const { runCommand, isWindows, findCorePackageDir } = require('./misc'); const debug = require('debug')('zapier:build'); @@ -332,6 +332,7 @@ const _buildFunc = async ({ osTmpDir, 'zapier-' + crypto.randomBytes(4).toString('hex') ); + debug('Using temp directory: ', tmpDir); maybeNotifyAboutOutdated(); @@ -342,9 +343,28 @@ const _buildFunc = async ({ await ensureDir(constants.BUILD_DIR); startSpinner('Copying project to temp directory'); - await copyDir(wdir, tmpDir, { - filter: skipNpmInstall ? (dir) => !dir.includes('.zip') : undefined, - }); + + const copyFilter = skipNpmInstall + ? (src) => !src.includes('.zip') + : undefined; + + await copyDir(wdir, tmpDir, { filter: copyFilter }); + + if (skipNpmInstall) { + const corePackageDir = findCorePackageDir(); + const nodeModulesDir = path.dirname(corePackageDir); + const workspaceDir = path.dirname(nodeModulesDir); + if (wdir !== workspaceDir) { + // If we're in here, it means the user is using npm/yarn workspaces + await copyDir(nodeModulesDir, path.join(tmpDir, 'node_modules'), { + filter: copyFilter, + onDirExists: (dir) => { + // Don't overwrite existing sub-directories in node_modules + return false; + }, + }); + } + } let output = {}; if (!skipNpmInstall) { @@ -412,7 +432,7 @@ const _buildFunc = async ({ * (Remote - `validateApp`) Both the Schema, AppVersion, and Auths are validated */ - startSpinner('Validating project schema'); + startSpinner('Validating project schema and style'); const validateResponse = await _appCommandZapierWrapper(tmpDir, { command: 'validate', }); @@ -425,8 +445,6 @@ const _buildFunc = async ({ ); } - startSpinner('Validating project style'); - // No need to mention specifically we're validating style checks as that's // implied from `zapier validate`, though it happens as a separate process const styleChecksResponse = await validateApp(rawDefinition); diff --git a/packages/cli/src/utils/files.js b/packages/cli/src/utils/files.js index c29fd341c..d3016352f 100644 --- a/packages/cli/src/utils/files.js +++ b/packages/cli/src/utils/files.js @@ -84,10 +84,10 @@ const copyFile = (src, dest, mode) => { }; // Returns a promise that copies a directory. -const copyDir = (src, dst, options) => { - const defaultFilter = (dir) => { - const isntPackage = dir.indexOf('node_modules') === -1; - const isntBuild = dir.indexOf('.zip') === -1; +const copyDir = async (src, dst, options) => { + const defaultFilter = (srcPath) => { + const isntPackage = !srcPath.includes('node_modules'); + const isntBuild = !srcPath.endsWith('.zip'); return isntPackage && isntBuild; }; @@ -96,43 +96,46 @@ const copyDir = (src, dst, options) => { filter: defaultFilter, onCopy: () => {}, onSkip: () => {}, + onDirExists: () => true, }); - return ensureDir(dst) - .then(() => fse.readdir(src)) - .then((files) => { - const promises = files.map((file) => { - const srcItem = path.resolve(src, file); - const dstItem = path.resolve(dst, file); - const stat = fse.statSync(srcItem); - const dstExists = fileExistsSync(dstItem); - - if (!options.filter(srcItem)) { - return Promise.resolve(); - } + await ensureDir(dst); + const files = await fse.readdirSync(src); - if (dstExists && options.clobber) { - fse.removeSync(dstItem); - } else if (dstExists) { - if (!stat.isDirectory()) { - options.onSkip(dstItem); - return Promise.resolve(); - } - } + const promises = files.map(async (file) => { + const srcItem = path.resolve(src, file); + const dstItem = path.resolve(dst, file); + const stat = fse.statSync(srcItem); + const isFile = stat.isFile(); + const dstExists = fileExistsSync(dstItem); + + if (!options.filter(srcItem)) { + return null; + } - if (stat.isDirectory()) { - return ensureDir(dstItem).then(() => - copyDir(srcItem, dstItem, options) - ); - } else { - return copyFile(srcItem, dstItem, stat.mode).then(() => { - options.onCopy(dstItem); - }); + if (isFile) { + if (dstExists) { + if (!options.clobber) { + options.onSkip(dstItem); + return null; } - }); + fse.removeSync(dstItem); + } - return Promise.all(promises); - }); + await copyFile(srcItem, dstItem, stat.mode); + options.onCopy(dstItem); + } else { + let shouldCopyRecursively = true; + if (dstExists) { + shouldCopyRecursively = options.onDirExists(dstItem); + } + if (shouldCopyRecursively) { + await copyDir(srcItem, dstItem, options); + } + } + }); + + return Promise.all(promises); }; // Delete a directory. diff --git a/packages/cli/src/utils/misc.js b/packages/cli/src/utils/misc.js index 5f576aee6..951e98441 100644 --- a/packages/cli/src/utils/misc.js +++ b/packages/cli/src/utils/misc.js @@ -77,6 +77,22 @@ const runCommand = (command, args, options) => { const isValidNodeVersion = (version = process.version) => semver.satisfies(version, NODE_VERSION_CLI_REQUIRES); +const findCorePackageDir = () => { + let baseDir = process.cwd(); + // 500 is just an arbitrary number to prevent infinite loops + for (let i = 0; i < 500; i++) { + const dir = path.join(baseDir, 'node_modules', PLATFORM_PACKAGE); + if (fse.existsSync(dir)) { + return dir; + } + if (baseDir === '/' || baseDir.match(/^[a-z]:\\$/i)) { + break; + } + baseDir = path.dirname(baseDir); + } + throw new Error(`Could not find ${PLATFORM_PACKAGE}.`); +}; + const isValidAppInstall = () => { let packageJson, dependedCoreVersion; try { @@ -91,7 +107,7 @@ const isValidAppInstall = () => { valid: false, reason: `Your app doesn't depend on ${PLATFORM_PACKAGE}. Run \`${colors.cyan( `npm install -E ${PLATFORM_PACKAGE}` - )}\` to resolve`, + )}\` to resolve.`, }; } else if (!semver.valid(dependedCoreVersion)) { // semver.valid only matches exact versions @@ -104,30 +120,30 @@ const isValidAppInstall = () => { return { valid: false, reason: String(err) }; } + let corePackageDir; try { - const installedPackageJson = require(path.join( - process.cwd(), - 'node_modules', - PLATFORM_PACKAGE, - 'package.json' - )); - - const installedCoreVersion = installedPackageJson.version; - // not an error for now, but something to mention to them - if (dependedCoreVersion !== installedCoreVersion) { - console.warn( - `\nYour code depends on v${dependedCoreVersion} of ${PLATFORM_PACKAGE}, but your local copy is v${installedCoreVersion}. You should probably reinstall your dependencies.\n` - ); - } + corePackageDir = findCorePackageDir(); } catch (err) { return { valid: false, reason: `Looks like you're missing a local installation of ${PLATFORM_PACKAGE}. Run \`${colors.cyan( 'npm install' - )}\` to resolve`, + )}\` to resolve.`, }; } + const installedPackageJson = require(path.join( + corePackageDir, + 'package.json' + )); + const installedCoreVersion = installedPackageJson.version; + + if (installedCoreVersion !== dependedCoreVersion) { + console.warn( + `\nYour code depends on v${dependedCoreVersion} of ${PLATFORM_PACKAGE}, but your local copy is v${installedCoreVersion}. You should probably reinstall your dependencies.\n` + ); + } + return { valid: true }; }; @@ -214,6 +230,7 @@ const printVersionInfo = (context) => { module.exports = { camelCase, entryPoint, + findCorePackageDir, isValidAppInstall, isValidNodeVersion, isWindows, From 05ac7b759a80909a8cc661451a224f52f9dbbc1e Mon Sep 17 00:00:00 2001 From: Chang-Hung Liang Date: Wed, 31 Jan 2024 15:50:12 +0800 Subject: [PATCH 02/12] Add tests for `zapier build` in workspaces --- packages/cli/package.json | 1 + packages/cli/src/tests/utils/build.js | 189 +++++++++++++++++++++++++- packages/cli/src/tests/utils/files.js | 155 +++++++++++++++++---- packages/cli/src/utils/build.js | 61 ++++++--- packages/cli/src/utils/files.js | 31 ++++- 5 files changed, 393 insertions(+), 44 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 903c02d00..a90bba7a4 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -32,6 +32,7 @@ "lint": "eslint src snippets", "lint:fix": "eslint --fix src snippets", "test": "cross-env NODE_ENV=test mocha -t 50s --recursive src/tests --exit", + "test-debug": "cross-env NODE_ENV=test node inspect ../../node_modules/.bin/mocha -t 50s --recursive src/tests --exit", "smoke-test": "cross-env NODE_ENV=test mocha -t 2m --recursive src/smoke-tests --exit", "validate-templates": "./scripts/validate-app-templates.js", "set-template-versions": "./scripts/set-app-template-versions.js", diff --git a/packages/cli/src/tests/utils/build.js b/packages/cli/src/tests/utils/build.js index 13ed751fb..df18951e6 100644 --- a/packages/cli/src/tests/utils/build.js +++ b/packages/cli/src/tests/utils/build.js @@ -33,7 +33,9 @@ describe('build (runs slowly)', function () { runCommand('npm', ['i'], { cwd: tmpDir }); // TODO: This test depends on how "typescript" example is set up, which // isn't good. Should refactor not to rely on that. - runCommand('npm', ['run', 'build', '--scripts-prepend-node-path'], { cwd: tmpDir }); + runCommand('npm', ['run', 'build', '--scripts-prepend-node-path'], { + cwd: tmpDir, + }); entryPoint = path.resolve(tmpDir, 'index.js'); }); @@ -309,3 +311,188 @@ describe('build (runs slowly)', function () { should.equal(buildExists, true); }); }); + +describe('build in workspaces', function () { + let tmpDir, origCwd; + + before(async () => { + tmpDir = getNewTempDirPath(); + + // Set up a monorepo project structure with two integrations as npm + // workspaces: + // + // packages/ + // ├─ package.json + // ├─ app-1/ + // │ ├─ index.js + // │ └─ package.json + // └─ app-2/ + // ├─ index.js + // └─ package.json + + // Create root package.json + fs.outputFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ + name: 'my-monorepo', + workspaces: ['packages/*'], + private: true, + }) + ); + + const defaultIndexJs = `module.exports = { + version: require('./package.json').version, + platformVersion: require('zapier-platform-core').version, +};`; + + // First integration: app-1 + fs.outputFileSync( + path.join(tmpDir, 'packages', 'app-1', 'index.js'), + defaultIndexJs + ); + fs.outputFileSync( + path.join(tmpDir, 'packages', 'app-1', 'package.json'), + JSON.stringify({ + name: 'app-1', + version: '1.0.0', + main: 'index.js', + dependencies: { + uuid: '8.3.2', + 'zapier-platform-core': '15.5.1', + }, + private: true, + }) + ); + + // Second integration: app-2 + fs.outputFileSync( + path.join(tmpDir, 'packages', 'app-2', 'index.js'), + defaultIndexJs + ); + fs.outputFileSync( + path.join(tmpDir, 'packages', 'app-2', 'package.json'), + JSON.stringify({ + name: 'app-2', + version: '1.0.0', + main: 'index.js', + dependencies: { + uuid: '9.0.1', + 'zapier-platform-core': '15.5.1', + }, + private: true, + }) + ); + + runCommand('yarn', ['install'], { cwd: tmpDir }); + }); + + after(() => { + fs.removeSync(tmpDir); + }); + + beforeEach(() => { + origCwd = process.cwd(); + }); + + afterEach(() => { + process.chdir(origCwd); + }); + + it('should build in app-1', async () => { + const workspaceDir = path.join(tmpDir, 'packages', 'app-1'); + const zipPath = path.join(workspaceDir, 'build', 'build.zip'); + const unzipPath = path.join(tmpDir, 'build', 'build'); + + // Make sure the zapier-platform-core dependency is installed in the root + // project directory + fs.existsSync( + path.join(tmpDir, 'node_modules', 'zapier-platform-core') + ).should.be.true(); + fs.existsSync( + path.join(workspaceDir, 'node_modules', 'zapier-platform-core') + ).should.be.false(); + + fs.ensureDirSync(path.dirname(zipPath)); + + process.chdir(workspaceDir); + + await build.buildAndOrUpload( + { build: true, upload: false }, + { + skipNpmInstall: true, + skipValidation: true, + printProgress: false, + checkOutdated: false, + } + ); + await decompress(zipPath, unzipPath); + + const corePackageJson = JSON.parse( + fs.readFileSync( + path.join( + unzipPath, + 'node_modules', + 'zapier-platform-core', + 'package.json' + ) + ) + ); + corePackageJson.version.should.equal('15.5.1'); + + const uuidPackageJson = JSON.parse( + fs.readFileSync( + path.join(unzipPath, 'node_modules', 'uuid', 'package.json') + ) + ); + uuidPackageJson.version.should.equal('8.3.2'); + }); + + it('should build in app-2', async () => { + const workspaceDir = path.join(tmpDir, 'packages', 'app-2'); + const zipPath = path.join(workspaceDir, 'build', 'build.zip'); + const unzipPath = path.join(tmpDir, 'build', 'build'); + + // Make sure the zapier-platform-core dependency is installed in the root + // project directory + fs.existsSync( + path.join(tmpDir, 'node_modules', 'zapier-platform-core') + ).should.be.true(); + fs.existsSync( + path.join(workspaceDir, 'node_modules', 'zapier-platform-core') + ).should.be.false(); + + fs.ensureDirSync(path.dirname(zipPath)); + + process.chdir(workspaceDir); + + await build.buildAndOrUpload( + { build: true, upload: false }, + { + skipNpmInstall: true, + skipValidation: true, + printProgress: false, + checkOutdated: false, + } + ); + await decompress(zipPath, unzipPath); + + const corePackageJson = JSON.parse( + fs.readFileSync( + path.join( + unzipPath, + 'node_modules', + 'zapier-platform-core', + 'package.json' + ) + ) + ); + corePackageJson.version.should.equal('15.5.1'); + + const uuidPackageJson = JSON.parse( + fs.readFileSync( + path.join(unzipPath, 'node_modules', 'uuid', 'package.json') + ) + ); + uuidPackageJson.version.should.equal('9.0.1'); + }); +}); diff --git a/packages/cli/src/tests/utils/files.js b/packages/cli/src/tests/utils/files.js index 3a4973521..19c48179b 100644 --- a/packages/cli/src/tests/utils/files.js +++ b/packages/cli/src/tests/utils/files.js @@ -1,8 +1,7 @@ -require('should'); -const files = require('../../utils/files'); - -const path = require('path'); const os = require('os'); +const path = require('path'); +const should = require('should'); +const files = require('../../utils/files'); describe('files', () => { let tmpDir; @@ -38,27 +37,137 @@ describe('files', () => { .catch(done); }); - // TODO: this is broken in travis - works locally though - it.skip('should copy a directory', (done) => { - const srcDir = os.tmpdir(); - const srcFileName = path.resolve(srcDir, 'read-write-test.txt'); - const dstDir = path.resolve(srcDir, 'zapier-platform-cli-test-dest-dir'); - const dstFileName = path.resolve(dstDir, 'read-write-test.txt'); - const data = '123'; + describe('copyDir', () => { + let tmpDir, srcDir, dstDir; - files - .writeFile(srcFileName, data) - .then(files.copyDir(srcDir, dstDir)) - .then(() => - files.readFile(dstFileName).then((buf) => { - buf.toString().should.equal(data); - done(); - }) - ) - .catch((err) => { - console.log('error', err); - done(err); + beforeEach(async () => { + tmpDir = path.join(os.tmpdir(), 'zapier-platform-cli-copyDir-test'); + srcDir = path.join(tmpDir, 'src'); + dstDir = path.join(tmpDir, 'dst'); + + await files.removeDir(srcDir); + await files.ensureDir(srcDir); + await files.writeFile(path.join(srcDir, '01.txt'), 'chapter 1'); + await files.writeFile(path.join(srcDir, '02.txt'), 'chapter 2'); + await files.ensureDir(path.join(srcDir, '03')); + await files.writeFile(path.join(srcDir, '03', '03.txt'), 'chapter 3'); + await files.writeFile(path.join(srcDir, '03', 'cover.jpg'), 'image data'); + await files.writeFile(path.join(srcDir, '03', 'photo.jpg'), 'photo data'); + + await files.removeDir(dstDir); + await files.ensureDir(dstDir); + await files.writeFile(path.join(dstDir, '01.txt'), 'ch 1'); + await files.writeFile(path.join(dstDir, '02.txt'), 'ch 2'); + await files.ensureDir(path.join(dstDir, '03')); + await files.writeFile(path.join(dstDir, '03', '03.txt'), 'ch 3'); + await files.writeFile(path.join(dstDir, '03', 'cover.jpg'), 'old data'); + await files.writeFile(path.join(dstDir, '03', 'fig.png'), 'png data'); + }); + + afterEach(async () => { + await files.removeDir(tmpDir); + }); + + it('should copy a directory without clobber', async () => { + // clobber defaults to false + await files.copyDir(srcDir, dstDir); + should.equal( + await files.readFileStr(path.join(dstDir, '01.txt')), + 'ch 1' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', '03.txt')), + 'ch 3' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', 'fig.png')), + 'png data' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', 'photo.jpg')), + 'photo data' + ); + }); + + it('should copy a directory with clobber', async () => { + await files.copyDir(srcDir, dstDir, { clobber: true }); + should.equal( + await files.readFileStr(path.join(dstDir, '02.txt')), + 'chapter 2' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', '03.txt')), + 'chapter 3' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', 'cover.jpg')), + 'image data' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', 'fig.png')), + 'png data' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', 'photo.jpg')), + 'photo data' + ); + }); + + it('should copy a directory with onDirExists returning false', async () => { + await files.copyDir(srcDir, dstDir, { + clobber: true, + onDirExists: () => false, }); + should.equal( + await files.readFileStr(path.join(dstDir, '02.txt')), + 'chapter 2' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', '03.txt')), + 'ch 3' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', 'cover.jpg')), + 'old data' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', 'fig.png')), + 'png data' + ); + files + .fileExistsSync(path.join(dstDir, '03', 'photo.jpg')) + .should.be.false(); + }); + + it('should copy a directory with onDirExists deleting dir', async () => { + await files.copyDir(srcDir, dstDir, { + clobber: true, + onDirExists: (dir) => { + // Delete existing directory => completely overwrite the directory + files.removeDirSync(dir); + return true; + }, + }); + should.equal( + await files.readFileStr(path.join(dstDir, '01.txt')), + 'chapter 1' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', '03.txt')), + 'chapter 3' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', 'cover.jpg')), + 'image data' + ); + should.equal( + await files.readFileStr(path.join(dstDir, '03', 'photo.jpg')), + 'photo data' + ); + files + .fileExistsSync(path.join(dstDir, '03', 'fig.png')) + .should.be.false(); + }); }); describe('validateFileExists', () => { diff --git a/packages/cli/src/utils/build.js b/packages/cli/src/utils/build.js index ebc92e180..ecfbfe351 100644 --- a/packages/cli/src/utils/build.js +++ b/packages/cli/src/utils/build.js @@ -322,6 +322,8 @@ const _buildFunc = async ({ skipNpmInstall = false, disableDependencyDetection = false, skipValidation = false, + printProgress = true, + checkOutdated = true, } = {}) => { const zipPath = constants.BUILD_PATH; const sourceZipPath = constants.SOURCE_PATH; @@ -334,7 +336,9 @@ const _buildFunc = async ({ ); debug('Using temp directory: ', tmpDir); - maybeNotifyAboutOutdated(); + if (checkOutdated) { + maybeNotifyAboutOutdated(); + } await maybeRunBuildScript(); @@ -342,7 +346,9 @@ const _buildFunc = async ({ await ensureDir(tmpDir); await ensureDir(constants.BUILD_DIR); - startSpinner('Copying project to temp directory'); + if (printProgress) { + startSpinner('Copying project to temp directory'); + } const copyFilter = skipNpmInstall ? (src) => !src.includes('.zip') @@ -368,8 +374,10 @@ const _buildFunc = async ({ let output = {}; if (!skipNpmInstall) { - endSpinner(); - startSpinner('Installing project dependencies'); + if (printProgress) { + endSpinner(); + startSpinner('Installing project dependencies'); + } output = await runCommand('npm', ['install', '--production'], { cwd: tmpDir, }); @@ -386,9 +394,12 @@ const _buildFunc = async ({ 'Could not install dependencies properly. Error log:\n' + output.stderr ); } - endSpinner(); - startSpinner('Applying entry point file'); + if (printProgress) { + endSpinner(); + startSpinner('Applying entry point file'); + } + // TODO: should this routine for include exist elsewhere? const zapierWrapperBuf = await readFile( path.join( @@ -403,9 +414,7 @@ const _buildFunc = async ({ path.join(tmpDir, 'zapierwrapper.js'), zapierWrapperBuf.toString() ); - endSpinner(); - startSpinner('Building app definition.json'); const rawDefinition = ( await _appCommandZapierWrapper(tmpDir, { command: 'definition', @@ -423,7 +432,10 @@ const _buildFunc = async ({ `Unable to write ${tmpDir}/definition.json, please check file permissions!` ); } - endSpinner(); + + if (printProgress) { + endSpinner(); + } if (!skipValidation) { /** @@ -432,7 +444,9 @@ const _buildFunc = async ({ * (Remote - `validateApp`) Both the Schema, AppVersion, and Auths are validated */ - startSpinner('Validating project schema and style'); + if (printProgress) { + startSpinner('Validating project schema and style'); + } const validateResponse = await _appCommandZapierWrapper(tmpDir, { command: 'validate', }); @@ -459,7 +473,9 @@ const _buildFunc = async ({ 'We hit some style validation errors, try running `zapier validate` to see them!' ); } - endSpinner(); + if (printProgress) { + endSpinner(); + } if (_.get(styleChecksResponse, ['warnings', 'total_failures'])) { console.log(colors.yellow('WARNINGS:')); @@ -475,16 +491,21 @@ const _buildFunc = async ({ debug('\nWarning: Skipping Validation'); } - startSpinner('Zipping project and dependencies'); + if (printProgress) { + startSpinner('Zipping project and dependencies'); + } await makeZip(tmpDir, path.join(wdir, zipPath), disableDependencyDetection); await makeSourceZip( tmpDir, path.join(wdir, sourceZipPath), disableDependencyDetection ); - endSpinner(); - startSpinner('Testing build'); + if (printProgress) { + endSpinner(); + startSpinner('Testing build'); + } + if (!isWindows()) { // TODO err, what should we do on windows? @@ -497,11 +518,17 @@ const _buildFunc = async ({ { cwd: tmpDir } ); } - endSpinner(); - startSpinner('Cleaning up temp directory'); + if (printProgress) { + endSpinner(); + startSpinner('Cleaning up temp directory'); + } + await removeDir(tmpDir); - endSpinner(); + + if (printProgress) { + endSpinner(); + } return zipPath; }; diff --git a/packages/cli/src/utils/files.js b/packages/cli/src/utils/files.js index d3016352f..b749feb53 100644 --- a/packages/cli/src/utils/files.js +++ b/packages/cli/src/utils/files.js @@ -83,7 +83,25 @@ const copyFile = (src, dest, mode) => { }); }; -// Returns a promise that copies a directory. +/* + Returns a promise that copies a directory recursively. + + Options: + + - clobber: Overwrite existing files? Default is false. + - filter: + A function that returns true if the file should be copied. By default, it + ignores node_modules and .zip files. + - onCopy: + A function called when a file is copied. Takes the destination path as an + argument. + - onSkip: + A function called when a file is skipped. Takes the destination path as an + argument. + - onDirExists: + A function called when a directory exists. Takes the destination path as + an argument. Returns true to carry on copying. Returns false to skip. +*/ const copyDir = async (src, dst, options) => { const defaultFilter = (srcPath) => { const isntPackage = !srcPath.includes('node_modules'); @@ -91,13 +109,18 @@ const copyDir = async (src, dst, options) => { return isntPackage && isntBuild; }; - options = _.defaults(options || {}, { + options = { clobber: false, filter: defaultFilter, onCopy: () => {}, onSkip: () => {}, onDirExists: () => true, - }); + ...options, + }; + + if (!options.filter) { + options.filter = defaultFilter; + } await ensureDir(dst); const files = await fse.readdirSync(src); @@ -140,6 +163,7 @@ const copyDir = async (src, dst, options) => { // Delete a directory. const removeDir = (dir) => fse.remove(dir); +const removeDirSync = (dir) => fse.removeSync(dir); // Returns true if directory is empty, else false. // Rejects if directory does not exist. @@ -167,6 +191,7 @@ module.exports = { readFile, readFileStr, removeDir, + removeDirSync, validateFileExists, writeFile, copyFile, From 13973980e2cf7c28fa81a3b057b424f1bd459768 Mon Sep 17 00:00:00 2001 From: Chang-Hung Liang Date: Wed, 31 Jan 2024 16:01:32 +0800 Subject: [PATCH 03/12] Fix box drawing tree --- packages/cli/src/tests/utils/build.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/tests/utils/build.js b/packages/cli/src/tests/utils/build.js index df18951e6..3d1cd0310 100644 --- a/packages/cli/src/tests/utils/build.js +++ b/packages/cli/src/tests/utils/build.js @@ -321,14 +321,15 @@ describe('build in workspaces', function () { // Set up a monorepo project structure with two integrations as npm // workspaces: // - // packages/ + // (project root) // ├─ package.json - // ├─ app-1/ - // │ ├─ index.js - // │ └─ package.json - // └─ app-2/ - // ├─ index.js - // └─ package.json + // └── packages/ + // ├─ app-1/ + // │ ├─ index.js + // │ └─ package.json + // └─ app-2/ + // ├─ index.js + // └─ package.json // Create root package.json fs.outputFileSync( From 6cfae02c81f8c12b708437e462e401060ff7a7f0 Mon Sep 17 00:00:00 2001 From: Chang-Hung Liang Date: Wed, 31 Jan 2024 19:40:30 +0800 Subject: [PATCH 04/12] Make `zapier validate` support workspaces too --- packages/cli/src/utils/local.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/utils/local.js b/packages/cli/src/utils/local.js index da8c6ffb6..7010fd7eb 100644 --- a/packages/cli/src/utils/local.js +++ b/packages/cli/src/utils/local.js @@ -16,7 +16,7 @@ const getLocalAppHandler = ({ reload = false, baseEvent = {} } = {}) => { let appRaw, zapier; try { appRaw = require(entryPath); - zapier = require(`${rootPath}/node_modules/${PLATFORM_PACKAGE}`); + zapier = require(PLATFORM_PACKAGE); } catch (err) { // this err.stack doesn't give a nice traceback at all :-( // maybe we could do require('syntax-error') in the future From 4ff1ba9a13ddba4097da71a108606d7bfba30ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Negr=C3=B3n?= Date: Mon, 29 Jan 2024 17:02:12 -0400 Subject: [PATCH 05/12] check gitignore before copying to tmpDir --- docs/cli.html | 2 +- docs/cli.md | 2 +- packages/cli/docs/cli.html | 2 +- packages/cli/docs/cli.md | 2 +- packages/cli/src/oclif/commands/build.js | 2 +- packages/cli/src/utils/build.js | 24 ++++++++++++++++++++++++ packages/cli/src/utils/files.js | 2 +- 7 files changed, 30 insertions(+), 6 deletions(-) diff --git a/docs/cli.html b/docs/cli.html index 5573e7415..18bec1537 100644 --- a/docs/cli.html +++ b/docs/cli.html @@ -345,7 +345,7 @@

build

Usage: zapier build

This command does the following:

  • Creates a temporary folder

  • -
  • Copies all code into the temporary folder

    +
  • Copies all code (excluding files in .gitignore) into the temporary folder

  • Adds an entry point: zapierwrapper.js

  • diff --git a/docs/cli.md b/docs/cli.md index 434fafd2f..5388fe535 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -34,7 +34,7 @@ This command does the following: * Creates a temporary folder -* Copies all code into the temporary folder +* Copies all code (excluding files in .gitignore) into the temporary folder * Adds an entry point: `zapierwrapper.js` diff --git a/packages/cli/docs/cli.html b/packages/cli/docs/cli.html index 5573e7415..18bec1537 100644 --- a/packages/cli/docs/cli.html +++ b/packages/cli/docs/cli.html @@ -345,7 +345,7 @@

    build

    Usage: zapier build

    This command does the following:

    • Creates a temporary folder

    • -
    • Copies all code into the temporary folder

      +
    • Copies all code (excluding files in .gitignore) into the temporary folder

    • Adds an entry point: zapierwrapper.js

    • diff --git a/packages/cli/docs/cli.md b/packages/cli/docs/cli.md index 434fafd2f..5388fe535 100644 --- a/packages/cli/docs/cli.md +++ b/packages/cli/docs/cli.md @@ -34,7 +34,7 @@ This command does the following: * Creates a temporary folder -* Copies all code into the temporary folder +* Copies all code (excluding files in .gitignore) into the temporary folder * Adds an entry point: `zapierwrapper.js` diff --git a/packages/cli/src/oclif/commands/build.js b/packages/cli/src/oclif/commands/build.js index 1fbbf0e0e..a43bc8c65 100644 --- a/packages/cli/src/oclif/commands/build.js +++ b/packages/cli/src/oclif/commands/build.js @@ -48,7 +48,7 @@ BuildCommand.description = `Build a pushable zip from the current directory. This command does the following: * Creates a temporary folder -* Copies all code into the temporary folder +* Copies all code (excluding files in .gitignore) into the temporary folder * Adds an entry point: \`zapierwrapper.js\` * Generates and validates app definition. * Detects dependencies via browserify (optional, on by default) diff --git a/packages/cli/src/utils/build.js b/packages/cli/src/utils/build.js index ecfbfe351..2b4bedffe 100644 --- a/packages/cli/src/utils/build.js +++ b/packages/cli/src/utils/build.js @@ -318,6 +318,29 @@ const maybeRunBuildScript = async (options = {}) => { } }; +const buildCopyDirFilter = ({ wdir, skipNpmInstall = false }) => { + // read and parse .gitignore + const gitIgnorePath = path.join(wdir, '.gitignore'); + const gitIgnoreFilter = ignore(); + + // create an ignore filter from .gitignore, if it exists + if (fs.existsSync(gitIgnorePath)) { + const gitIgnoredPaths = gitIgnore(gitIgnorePath); + const validGitIgnorePaths = gitIgnoredPaths.filter(ignore.isPathValid); + gitIgnoreFilter.add(validGitIgnorePaths); + } + + return (file) => { + // exclude any files defined in .gitignore + if (gitIgnoreFilter.ignores(path.relative(wdir, file))) { + return false; + } + + // exclude '.zip' files only if skipNpmInstall is true + return !(skipNpmInstall && file.includes('.zip')); + }; +}; + const _buildFunc = async ({ skipNpmInstall = false, disableDependencyDetection = false, @@ -563,4 +586,5 @@ module.exports = { listFiles, requiredFiles, maybeRunBuildScript, + buildCopyDirFilter, }; diff --git a/packages/cli/src/utils/files.js b/packages/cli/src/utils/files.js index b749feb53..997f89798 100644 --- a/packages/cli/src/utils/files.js +++ b/packages/cli/src/utils/files.js @@ -87,7 +87,7 @@ const copyFile = (src, dest, mode) => { Returns a promise that copies a directory recursively. Options: - + - clobber: Overwrite existing files? Default is false. - filter: A function that returns true if the file should be copied. By default, it From dff25dd726d6ea0a9b54904ec6aa91f5556df07e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Negr=C3=B3n?= Date: Wed, 31 Jan 2024 14:45:33 -0400 Subject: [PATCH 06/12] Revert "check gitignore before copying to tmpDir" This reverts commit 4ff1ba9a13ddba4097da71a108606d7bfba30ea4. --- docs/cli.html | 2 +- docs/cli.md | 2 +- packages/cli/docs/cli.html | 2 +- packages/cli/docs/cli.md | 2 +- packages/cli/src/oclif/commands/build.js | 2 +- packages/cli/src/utils/build.js | 24 ------------------------ packages/cli/src/utils/files.js | 2 +- 7 files changed, 6 insertions(+), 30 deletions(-) diff --git a/docs/cli.html b/docs/cli.html index 18bec1537..5573e7415 100644 --- a/docs/cli.html +++ b/docs/cli.html @@ -345,7 +345,7 @@

      build

      Usage: zapier build

      This command does the following:

      • Creates a temporary folder

      • -
      • Copies all code (excluding files in .gitignore) into the temporary folder

        +
      • Copies all code into the temporary folder

      • Adds an entry point: zapierwrapper.js

      • diff --git a/docs/cli.md b/docs/cli.md index 5388fe535..434fafd2f 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -34,7 +34,7 @@ This command does the following: * Creates a temporary folder -* Copies all code (excluding files in .gitignore) into the temporary folder +* Copies all code into the temporary folder * Adds an entry point: `zapierwrapper.js` diff --git a/packages/cli/docs/cli.html b/packages/cli/docs/cli.html index 18bec1537..5573e7415 100644 --- a/packages/cli/docs/cli.html +++ b/packages/cli/docs/cli.html @@ -345,7 +345,7 @@

        build

        Usage: zapier build

        This command does the following:

        • Creates a temporary folder

        • -
        • Copies all code (excluding files in .gitignore) into the temporary folder

          +
        • Copies all code into the temporary folder

        • Adds an entry point: zapierwrapper.js

        • diff --git a/packages/cli/docs/cli.md b/packages/cli/docs/cli.md index 5388fe535..434fafd2f 100644 --- a/packages/cli/docs/cli.md +++ b/packages/cli/docs/cli.md @@ -34,7 +34,7 @@ This command does the following: * Creates a temporary folder -* Copies all code (excluding files in .gitignore) into the temporary folder +* Copies all code into the temporary folder * Adds an entry point: `zapierwrapper.js` diff --git a/packages/cli/src/oclif/commands/build.js b/packages/cli/src/oclif/commands/build.js index a43bc8c65..1fbbf0e0e 100644 --- a/packages/cli/src/oclif/commands/build.js +++ b/packages/cli/src/oclif/commands/build.js @@ -48,7 +48,7 @@ BuildCommand.description = `Build a pushable zip from the current directory. This command does the following: * Creates a temporary folder -* Copies all code (excluding files in .gitignore) into the temporary folder +* Copies all code into the temporary folder * Adds an entry point: \`zapierwrapper.js\` * Generates and validates app definition. * Detects dependencies via browserify (optional, on by default) diff --git a/packages/cli/src/utils/build.js b/packages/cli/src/utils/build.js index 2b4bedffe..ecfbfe351 100644 --- a/packages/cli/src/utils/build.js +++ b/packages/cli/src/utils/build.js @@ -318,29 +318,6 @@ const maybeRunBuildScript = async (options = {}) => { } }; -const buildCopyDirFilter = ({ wdir, skipNpmInstall = false }) => { - // read and parse .gitignore - const gitIgnorePath = path.join(wdir, '.gitignore'); - const gitIgnoreFilter = ignore(); - - // create an ignore filter from .gitignore, if it exists - if (fs.existsSync(gitIgnorePath)) { - const gitIgnoredPaths = gitIgnore(gitIgnorePath); - const validGitIgnorePaths = gitIgnoredPaths.filter(ignore.isPathValid); - gitIgnoreFilter.add(validGitIgnorePaths); - } - - return (file) => { - // exclude any files defined in .gitignore - if (gitIgnoreFilter.ignores(path.relative(wdir, file))) { - return false; - } - - // exclude '.zip' files only if skipNpmInstall is true - return !(skipNpmInstall && file.includes('.zip')); - }; -}; - const _buildFunc = async ({ skipNpmInstall = false, disableDependencyDetection = false, @@ -586,5 +563,4 @@ module.exports = { listFiles, requiredFiles, maybeRunBuildScript, - buildCopyDirFilter, }; diff --git a/packages/cli/src/utils/files.js b/packages/cli/src/utils/files.js index 997f89798..b749feb53 100644 --- a/packages/cli/src/utils/files.js +++ b/packages/cli/src/utils/files.js @@ -87,7 +87,7 @@ const copyFile = (src, dest, mode) => { Returns a promise that copies a directory recursively. Options: - + - clobber: Overwrite existing files? Default is false. - filter: A function that returns true if the file should be copied. By default, it From c1a68fe962ef005a67e39af307257cfa986f258c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Negr=C3=B3n?= Date: Wed, 31 Jan 2024 16:29:24 -0400 Subject: [PATCH 07/12] rebase and use warning logic in copyDir --- packages/cli/src/utils/files.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/utils/files.js b/packages/cli/src/utils/files.js index b749feb53..e897390ff 100644 --- a/packages/cli/src/utils/files.js +++ b/packages/cli/src/utils/files.js @@ -4,6 +4,7 @@ const os = require('os'); const path = require('path'); const fse = require('fs-extra'); +const colors = require('colors/safe'); const fixHome = (dir) => { const home = process.env.HOME || process.env.USERPROFILE; @@ -87,7 +88,7 @@ const copyFile = (src, dest, mode) => { Returns a promise that copies a directory recursively. Options: - + - clobber: Overwrite existing files? Default is false. - filter: A function that returns true if the file should be copied. By default, it @@ -127,16 +128,27 @@ const copyDir = async (src, dst, options) => { const promises = files.map(async (file) => { const srcItem = path.resolve(src, file); + const srcStat = fse.lstatSync(srcItem); + const srcIsFile = srcStat.isFile(); + const srcIsSymbolicLink = srcStat.isSymbolicLink(); + const dstItem = path.resolve(dst, file); - const stat = fse.statSync(srcItem); - const isFile = stat.isFile(); const dstExists = fileExistsSync(dstItem); - if (!options.filter(srcItem)) { return null; } - if (isFile) { + if (srcIsFile || srcIsSymbolicLink) { + if (srcIsSymbolicLink && !fileExistsSync(srcItem)) { + console.warn( + colors.yellow( + `\n! Warning: symlink "${srcItem}" points to a non-existent file. Skipping!\n` + ) + ); + options.onSkip(dstItem); + return null; + } + if (dstExists) { if (!options.clobber) { options.onSkip(dstItem); @@ -145,7 +157,7 @@ const copyDir = async (src, dst, options) => { fse.removeSync(dstItem); } - await copyFile(srcItem, dstItem, stat.mode); + await copyFile(srcItem, dstItem, srcStat.mode); options.onCopy(dstItem); } else { let shouldCopyRecursively = true; From 7968c495a12c3c1af1922a8418f51efeca3bc20e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Negr=C3=B3n?= Date: Wed, 31 Jan 2024 18:25:39 -0400 Subject: [PATCH 08/12] simplify logic to: if stat fails, check if symlink and notify if so --- packages/cli/src/utils/files.js | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/utils/files.js b/packages/cli/src/utils/files.js index e897390ff..afd9daa0d 100644 --- a/packages/cli/src/utils/files.js +++ b/packages/cli/src/utils/files.js @@ -128,27 +128,34 @@ const copyDir = async (src, dst, options) => { const promises = files.map(async (file) => { const srcItem = path.resolve(src, file); - const srcStat = fse.lstatSync(srcItem); - const srcIsFile = srcStat.isFile(); - const srcIsSymbolicLink = srcStat.isSymbolicLink(); - const dstItem = path.resolve(dst, file); - const dstExists = fileExistsSync(dstItem); - if (!options.filter(srcItem)) { - return null; - } - - if (srcIsFile || srcIsSymbolicLink) { - if (srcIsSymbolicLink && !fileExistsSync(srcItem)) { + let srcStat; + try { + srcStat = fse.statSync(srcItem); + } catch (err) { + // If the file is a symlink and the target doesn't exist, skip it. + if (fse.lstatSync(srcItem).isSymbolicLink()) { console.warn( colors.yellow( `\n! Warning: symlink "${srcItem}" points to a non-existent file. Skipping!\n` ) ); - options.onSkip(dstItem); return null; } + // otherwise, rethrow the error + throw err; + } + + const srcIsFile = srcStat.isFile(); + + const dstItem = path.resolve(dst, file); + const dstExists = fileExistsSync(dstItem); + if (!options.filter(srcItem)) { + return null; + } + + if (srcIsFile) { if (dstExists) { if (!options.clobber) { options.onSkip(dstItem); From 52fba09b195add7cd30fa7ed19efeb8775fbf31c Mon Sep 17 00:00:00 2001 From: Chang-Hung Liang Date: Thu, 1 Feb 2024 14:05:18 +0800 Subject: [PATCH 09/12] Add startSpinner back --- packages/cli/src/utils/build.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/cli/src/utils/build.js b/packages/cli/src/utils/build.js index ecfbfe351..ad55b6430 100644 --- a/packages/cli/src/utils/build.js +++ b/packages/cli/src/utils/build.js @@ -415,6 +415,11 @@ const _buildFunc = async ({ zapierWrapperBuf.toString() ); + if (printProgress) { + endSpinner(); + startSpinner('Building app definition.json'); + } + const rawDefinition = ( await _appCommandZapierWrapper(tmpDir, { command: 'definition', From a826667040f43bec5eeedc86ce6c368959a11e4f Mon Sep 17 00:00:00 2001 From: Chang-Hung Liang Date: Thu, 1 Feb 2024 14:08:04 +0800 Subject: [PATCH 10/12] Use spaces instead tabs --- packages/cli/src/utils/files.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/cli/src/utils/files.js b/packages/cli/src/utils/files.js index b749feb53..83cbc4dec 100644 --- a/packages/cli/src/utils/files.js +++ b/packages/cli/src/utils/files.js @@ -86,21 +86,21 @@ const copyFile = (src, dest, mode) => { /* Returns a promise that copies a directory recursively. - Options: - - - clobber: Overwrite existing files? Default is false. - - filter: - A function that returns true if the file should be copied. By default, it - ignores node_modules and .zip files. - - onCopy: - A function called when a file is copied. Takes the destination path as an - argument. - - onSkip: - A function called when a file is skipped. Takes the destination path as an - argument. - - onDirExists: - A function called when a directory exists. Takes the destination path as - an argument. Returns true to carry on copying. Returns false to skip. + Options: + + - clobber: Overwrite existing files? Default is false. + - filter: + A function that returns true if the file should be copied. By default, it + ignores node_modules and .zip files. + - onCopy: + A function called when a file is copied. Takes the destination path as an + argument. + - onSkip: + A function called when a file is skipped. Takes the destination path as an + argument. + - onDirExists: + A function called when a directory exists. Takes the destination path as + an argument. Returns true to carry on copying. Returns false to skip. */ const copyDir = async (src, dst, options) => { const defaultFilter = (srcPath) => { From dacbf6dae631ff88b2bf12c6c4d7d298114b7d13 Mon Sep 17 00:00:00 2001 From: Chang-Hung Liang Date: Thu, 1 Feb 2024 19:56:42 +0800 Subject: [PATCH 11/12] Exclude workspace symlinks from build.zip --- packages/cli/package.json | 1 + packages/cli/src/utils/build.js | 46 ++++++++++++++++++++++++++++++--- yarn.lock | 7 +++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index a90bba7a4..64bd777a1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -60,6 +60,7 @@ "lodash": "4.17.21", "marked": "4.2.12", "marked-terminal": "5.2.0", + "minimatch": "9.0.3", "node-fetch": "2.6.7", "ora": "5.4.0", "parse-gitignore": "0.5.1", diff --git a/packages/cli/src/utils/build.js b/packages/cli/src/utils/build.js index ad55b6430..fccb01f7a 100644 --- a/packages/cli/src/utils/build.js +++ b/packages/cli/src/utils/build.js @@ -14,6 +14,7 @@ const colors = require('colors/safe'); const ignore = require('ignore'); const gitIgnore = require('parse-gitignore'); const semver = require('semver'); +const { minimatch } = require('minimatch'); const { constants: { Z_BEST_COMPRESSION }, @@ -318,6 +319,24 @@ const maybeRunBuildScript = async (options = {}) => { } }; +const listWorkspaces = (workspaceRoot) => { + const packageJsonPath = path.join(workspaceRoot, 'package.json'); + if (!fs.existsSync(packageJsonPath)) { + return []; + } + + let packageJson; + try { + packageJson = require(packageJsonPath); + } catch (err) { + return []; + } + + return (packageJson.workspaces || []).map((relpath) => + path.join(workspaceRoot, relpath) + ); +}; + const _buildFunc = async ({ skipNpmInstall = false, disableDependencyDetection = false, @@ -351,7 +370,7 @@ const _buildFunc = async ({ } const copyFilter = skipNpmInstall - ? (src) => !src.includes('.zip') + ? (src) => !src.endsWith('.zip') : undefined; await copyDir(wdir, tmpDir, { filter: copyFilter }); @@ -359,11 +378,30 @@ const _buildFunc = async ({ if (skipNpmInstall) { const corePackageDir = findCorePackageDir(); const nodeModulesDir = path.dirname(corePackageDir); - const workspaceDir = path.dirname(nodeModulesDir); - if (wdir !== workspaceDir) { + const workspaceRoot = path.dirname(nodeModulesDir); + if (wdir !== workspaceRoot) { // If we're in here, it means the user is using npm/yarn workspaces + const workspaces = listWorkspaces(workspaceRoot); + await copyDir(nodeModulesDir, path.join(tmpDir, 'node_modules'), { - filter: copyFilter, + filter: (src) => { + if (src.endsWith('.zip')) { + return false; + } + const stat = fse.lstatSync(src); + if (stat.isSymbolicLink()) { + const realPath = path.resolve( + path.dirname(src), + fse.readlinkSync(src) + ); + for (const workspace of workspaces) { + if (minimatch(realPath, workspace)) { + return false; + } + } + } + return true; + }, onDirExists: (dir) => { // Don't overwrite existing sub-directories in node_modules return false; diff --git a/yarn.lock b/yarn.lock index 9cfb3e85d..5a081c2ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8129,6 +8129,13 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minimatch@^5.0.1: version "5.1.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" From d5f1c45de8033fa36659c7074b57330a7c8f3c99 Mon Sep 17 00:00:00 2001 From: Chang-Hung Liang Date: Thu, 1 Feb 2024 19:59:15 +0800 Subject: [PATCH 12/12] Add test code --- packages/cli/src/tests/utils/build.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/cli/src/tests/utils/build.js b/packages/cli/src/tests/utils/build.js index 3d1cd0310..7718a4a09 100644 --- a/packages/cli/src/tests/utils/build.js +++ b/packages/cli/src/tests/utils/build.js @@ -446,6 +446,15 @@ describe('build in workspaces', function () { ) ); uuidPackageJson.version.should.equal('8.3.2'); + + // Make sure node_modules/app-1 and node_modules/app-2 are not included + // in the build + fs.existsSync( + path.join(unzipPath, 'node_modules', 'app-1') + ).should.be.false(); + fs.existsSync( + path.join(unzipPath, 'node_modules', 'app-2') + ).should.be.false(); }); it('should build in app-2', async () => { @@ -495,5 +504,14 @@ describe('build in workspaces', function () { ) ); uuidPackageJson.version.should.equal('9.0.1'); + + // Make sure node_modules/app-1 and node_modules/app-2 are not included + // in the build + fs.existsSync( + path.join(unzipPath, 'node_modules', 'app-1') + ).should.be.false(); + fs.existsSync( + path.join(unzipPath, 'node_modules', 'app-2') + ).should.be.false(); }); });