From 651345afa80d9868c21611eb1561cf4030a3c612 Mon Sep 17 00:00:00 2001 From: tianyingchun <tianyingchun@outlook.com> Date: Fri, 21 Jun 2024 10:08:13 +0800 Subject: [PATCH] feat: add `.env.*` copy support --- .changeset/proud-parents-rescue.md | 5 ++ package.json | 1 + src/file-walk.ts | 16 +++++ src/next-standalone.ts | 10 ++++ tests/file-walk.spec.ts | 96 ++++++++++++++++++++++++++++++ tests/next-standalone.spec.ts | 37 +++++++++++- yarn.lock | 40 ++++++++++++- 7 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 .changeset/proud-parents-rescue.md create mode 100644 src/file-walk.ts create mode 100644 tests/file-walk.spec.ts diff --git a/.changeset/proud-parents-rescue.md b/.changeset/proud-parents-rescue.md new file mode 100644 index 0000000..f3e20af --- /dev/null +++ b/.changeset/proud-parents-rescue.md @@ -0,0 +1,5 @@ +--- +"@hyperse/hyper-env": patch +--- + +add `.env.*` copy support diff --git a/package.json b/package.json index 4ec460b..7c95a31 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "@vercel/nft": "^0.27.2", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", + "globby": "^14.0.1", "minimist": "^1.2.8" }, "devDependencies": { diff --git a/src/file-walk.ts b/src/file-walk.ts new file mode 100644 index 0000000..e2339c6 --- /dev/null +++ b/src/file-walk.ts @@ -0,0 +1,16 @@ +import type { Options } from 'globby'; +import { globby } from 'globby'; + +export const fileWalk = ( + pattern: string | readonly string[], + options: Options = {} +): Promise<string[]> => { + const ignorePattern = options.ignore || []; + return globby(pattern, { + absolute: false, + dot: true, + unique: true, + ...options, + ignore: [...ignorePattern, '**/__MACOSX/**', '**/*.DS_Store'], + }); +}; diff --git a/src/next-standalone.ts b/src/next-standalone.ts index b908b8b..e8635b8 100644 --- a/src/next-standalone.ts +++ b/src/next-standalone.ts @@ -3,6 +3,7 @@ import fsPromise from 'fs/promises'; import minimist from 'minimist'; import { dirname, resolve } from 'path'; import { nodeFileTrace } from '@vercel/nft'; +import { fileWalk } from './file-walk.js'; import { getDirname } from './get-dir-name.js'; type Argv = { @@ -38,6 +39,15 @@ export const nextStandalone = async (args: string[]) => { base: fromBase, }); + const envFiles = await fileWalk(['.env', '.env.*'], { + cwd: fromBase, + absolute: false, + }); + + for (const absEnvFile of envFiles) { + fileList.add(absEnvFile); + } + for (const filePath of fileList) { const copyTo = resolve(copyToBase, '.next/standalone', filePath); if (!fs.existsSync(dirname(copyTo))) { diff --git a/tests/file-walk.spec.ts b/tests/file-walk.spec.ts new file mode 100644 index 0000000..e616629 --- /dev/null +++ b/tests/file-walk.spec.ts @@ -0,0 +1,96 @@ +import { mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileWalk } from '../src/file-walk.js'; +import { getDirname } from '../src/get-dir-name.js'; + +const createFixtureFiles = ( + url: string, + dir = 'fixture', + files: Record<string, string> +) => { + const fixtureCwd = getDirname(url, dir); + mkdirSync(fixtureCwd, { + recursive: true, + }); + for (const [key, value] of Object.entries(files)) { + const item = join(fixtureCwd, key); + mkdirSync(dirname(item), { + recursive: true, + }); + writeFileSync(item, value ? value : 'hello' + Math.random()); + } + return fixtureCwd; +}; + +describe('fileWalk', () => { + let fixtureCwd: string; + const envFiles = { + '.env': '', + '.env.dev': '', + '.env.inte': '', + '.env.rc': '', + '.env.prod': '', + }; + beforeAll(() => { + fixtureCwd = createFixtureFiles(import.meta.url, 'filewalk', { + 'a/b/c/text.txt': '', + 'a/b/c/image.jpg': '', + 'a/b/c/image.png': '', + 'a/b/c/d/e/image.jpg': '', + 'a/b/c/d/e/image.png': '', + 'a/b/c/style.css': '', + 'a/b/c/.gitignore': '', + '__MACOSX/test/._demo-8ca86e6b.png': '', + '__MACOSX/test/demo-8ca86e6b.png': '', + '__MACOSX/test/assets/__MACOSX/test/._.DS_Store': '', + 'abc/__MACOSX/test/._demo-8ca86e6b.png': '', + 'abc/__MACOSX/test/demo-8ca86e6b.png': '', + 'abc/__MACOSX/test/assets/__MACOSX/test/._.DS_Store': '', + ...envFiles, + }); + }); + + afterAll(() => { + rmSync(fixtureCwd, { + force: true, + recursive: true, + }); + }); + + describe('fileWalkAsync', () => { + it('should asynchronously support correct globby patterns & negative patterns', async () => { + const files = await fileWalk('**/*.*', { + cwd: fixtureCwd, + ignore: ['**/*.{jpg,png}'], + }); + expect(files.length).toBe(8); + expect(files.filter((s) => s.endsWith('.gitignore')).length).toBe(1); + }); + + it('should asynchronously currect handle dot files', async () => { + const files = await fileWalk('**/*.*', { + cwd: fixtureCwd, + }); + expect(files.length).toBe(12); + expect(files.filter((s) => s.endsWith('.gitignore')).length).toBe(1); + }); + + it('should asynchronously ignore __MACOSX & .DS_Store', async () => { + const files = await fileWalk('**/*.*', { + cwd: fixtureCwd, + }); + expect(files.length).toBe(12); + expect(files.filter((s) => s.endsWith('.gitignore')).length).toBe(1); + }); + + it('should asynchronously list .env fules', async () => { + const files = await fileWalk(['.env', '.env.*'], { + cwd: fixtureCwd, + }); + expect(files.length).toBe(5); + for (const [expectEnvFile] of Object.entries(envFiles)) { + expect(files.find((s) => s.endsWith(expectEnvFile))).toBeDefined(); + } + }); + }); +}); diff --git a/tests/next-standalone.spec.ts b/tests/next-standalone.spec.ts index 8e9a016..4a84205 100644 --- a/tests/next-standalone.spec.ts +++ b/tests/next-standalone.spec.ts @@ -1,11 +1,31 @@ -import fs, { rmdirSync } from 'fs'; +import fs, { rmdirSync, rmSync, writeFileSync } from 'fs'; import fsPromise from 'fs/promises'; import { join } from 'path'; import { getDirname } from '../src/get-dir-name.js'; import { nextStandalone } from '../src/next-standalone.js'; describe('Next Standalone', () => { + const fixtureCwd = getDirname(import.meta.url); const binFile = getDirname(import.meta.url, '../bin/hyper-env.mjs'); + const envFiles = { + '.env': '', + '.env.dev': '', + '.env.inte': '', + '.env.rc': '', + '.env.prod': '', + }; + + beforeAll(() => { + for (const [envFile] of Object.entries(envFiles)) { + writeFileSync(join(fixtureCwd, envFile), ''); + } + }); + + afterAll(() => { + for (const [envFile] of Object.entries(envFiles)) { + rmSync(join(fixtureCwd, envFile)); + } + }); beforeEach(() => { vi.spyOn(fs, 'existsSync').mockReturnValue(true); @@ -71,6 +91,21 @@ describe('Next Standalone', () => { } }); + it('should correct handle copy .env files for workdir', async () => { + await nextStandalone([ + '--fromBase', + fixtureCwd, + '--copyToBase', + fixtureCwd, + ]); + for (const envFile of Object.keys(envFiles)) { + expect(fsPromise.copyFile).toHaveBeenCalledWith( + join(fixtureCwd, envFile), + join(fixtureCwd, '.next/standalone', envFile) + ); + } + }); + it('should correct handle argv dummy standard parameters', async () => { const fromBase = '/fromBase'; const copyToBase = '/copyToBase'; diff --git a/yarn.lock b/yarn.lock index 6dced67..475ebfd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -822,6 +822,7 @@ __metadata: dotenv: "npm:^16.4.5" dotenv-expand: "npm:^11.0.6" eslint: "npm:^9.5.0" + globby: "npm:^14.0.1" husky: "npm:9.0.11" lint-staged: "npm:15.2.7" minimist: "npm:^1.2.8" @@ -1285,6 +1286,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/merge-streams@npm:^2.1.0": + version: 2.3.0 + resolution: "@sindresorhus/merge-streams@npm:2.3.0" + checksum: 10/798bcb53cd1ace9df84fcdd1ba86afdc9e0cd84f5758d26ae9b1eefd8e8887e5fc30051132b9e74daf01bb41fa5a2faf1369361f83d76a3b3d7ee938058fd71c + languageName: node + linkType: hard + "@sindresorhus/merge-streams@npm:^4.0.0": version: 4.0.0 resolution: "@sindresorhus/merge-streams@npm:4.0.0" @@ -3977,7 +3985,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.5, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0": +"fast-glob@npm:^3.2.5, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0, fast-glob@npm:^3.3.2": version: 3.3.2 resolution: "fast-glob@npm:3.3.2" dependencies: @@ -4513,6 +4521,20 @@ __metadata: languageName: node linkType: hard +"globby@npm:^14.0.1": + version: 14.0.1 + resolution: "globby@npm:14.0.1" + dependencies: + "@sindresorhus/merge-streams": "npm:^2.1.0" + fast-glob: "npm:^3.3.2" + ignore: "npm:^5.2.4" + path-type: "npm:^5.0.0" + slash: "npm:^5.1.0" + unicorn-magic: "npm:^0.1.0" + checksum: 10/b36f57afc45a857a884d82657603c7e1663b1e6f3f9afbeb53d12e42230469fc5b26a7e14a01e51086f3f25c138f58a7002036fcc8f3ca054097b6dd7c71d639 + languageName: node + linkType: hard + "gopd@npm:^1.0.1": version: 1.0.1 resolution: "gopd@npm:1.0.1" @@ -4734,7 +4756,7 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.0.0, ignore@npm:^5.2.0, ignore@npm:^5.3.1": +"ignore@npm:^5.0.0, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.1": version: 5.3.1 resolution: "ignore@npm:5.3.1" checksum: 10/0a884c2fbc8c316f0b9f92beaf84464253b73230a4d4d286697be45fca081199191ca33e1c2e82d9e5f851f5e9a48a78e25a35c951e7eb41e59f150db3530065 @@ -7320,6 +7342,13 @@ __metadata: languageName: node linkType: hard +"path-type@npm:^5.0.0": + version: 5.0.0 + resolution: "path-type@npm:5.0.0" + checksum: 10/15ec24050e8932c2c98d085b72cfa0d6b4eeb4cbde151a0a05726d8afae85784fc5544f733d8dfc68536587d5143d29c0bd793623fad03d7e61cc00067291cd5 + languageName: node + linkType: hard + "pathe@npm:^1.1.0, pathe@npm:^1.1.1": version: 1.1.1 resolution: "pathe@npm:1.1.1" @@ -8306,6 +8335,13 @@ __metadata: languageName: node linkType: hard +"slash@npm:^5.1.0": + version: 5.1.0 + resolution: "slash@npm:5.1.0" + checksum: 10/2c41ec6fb1414cd9bba0fa6b1dd00e8be739e3fe85d079c69d4b09ca5f2f86eafd18d9ce611c0c0f686428638a36c272a6ac14799146a8295f259c10cc45cde4 + languageName: node + linkType: hard + "slice-ansi@npm:^5.0.0": version: 5.0.0 resolution: "slice-ansi@npm:5.0.0"