@vxrn/vite-plugin-metro 2.0.0-beta.51.1 → 2.0.0-beta.56.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/env/platformEnv.cjs +2 -0
- package/dist/cjs/env/platformEnv.native.cjs +2 -0
- package/dist/cjs/env/platformEnv.native.js +2 -0
- package/dist/cjs/env/platformEnv.native.js.map +1 -1
- package/dist/cjs/index.cjs +0 -2
- package/dist/cjs/index.native.cjs +0 -2
- package/dist/cjs/index.native.js +0 -2
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/metro-config/getMetroBabelConfigFromViteConfig.cjs +10 -15
- package/dist/cjs/metro-config/getMetroBabelConfigFromViteConfig.native.cjs +27 -48
- package/dist/cjs/metro-config/getMetroBabelConfigFromViteConfig.native.js +27 -48
- package/dist/cjs/metro-config/getMetroBabelConfigFromViteConfig.native.js.map +1 -1
- package/dist/cjs/metro-config/getMetroConfigFromViteConfig.cjs +71 -38
- package/dist/cjs/metro-config/getMetroConfigFromViteConfig.native.cjs +86 -42
- package/dist/cjs/metro-config/getMetroConfigFromViteConfig.native.js +86 -42
- package/dist/cjs/metro-config/getMetroConfigFromViteConfig.native.js.map +1 -1
- package/dist/cjs/transformer/babel-transformer.cjs +53 -4
- package/dist/cjs/transformer/babel-transformer.native.cjs +65 -6
- package/dist/cjs/transformer/babel-transformer.native.js +65 -6
- package/dist/cjs/transformer/babel-transformer.native.js.map +1 -1
- package/dist/cjs/transformer/expoVirtualEnv.cjs +92 -0
- package/dist/cjs/transformer/expoVirtualEnv.native.cjs +113 -0
- package/dist/cjs/transformer/expoVirtualEnv.native.js +115 -0
- package/dist/cjs/transformer/expoVirtualEnv.native.js.map +1 -0
- package/dist/cjs/transformer/loadBabelConfig.cjs +1 -1
- package/dist/cjs/transformer/loadBabelConfig.native.cjs +1 -1
- package/dist/cjs/transformer/loadBabelConfig.native.js +1 -1
- package/dist/cjs/transformer/loadBabelConfig.native.js.map +1 -1
- package/dist/cjs/transformer/metroNativeWorker.cjs +20 -29
- package/dist/cjs/transformer/metroNativeWorker.native.cjs +24 -30
- package/dist/cjs/transformer/metroNativeWorker.native.js +24 -30
- package/dist/cjs/transformer/metroNativeWorker.native.js.map +1 -1
- package/dist/cjs/transformer/transformSync.cjs +3 -3
- package/dist/cjs/transformer/transformSync.native.cjs +3 -3
- package/dist/cjs/transformer/transformSync.native.js +3 -3
- package/dist/cjs/transformer/transformSync.native.js.map +1 -1
- package/dist/cjs/utils/exit.cjs +1 -1
- package/dist/cjs/utils/exit.native.cjs +1 -1
- package/dist/cjs/utils/exit.native.js +1 -1
- package/dist/cjs/utils/exit.native.js.map +1 -1
- package/dist/esm/env/platformEnv.mjs +2 -0
- package/dist/esm/env/platformEnv.mjs.map +1 -1
- package/dist/esm/env/platformEnv.native.js +2 -0
- package/dist/esm/env/platformEnv.native.js.map +1 -1
- package/dist/esm/index.js +1 -2
- package/dist/esm/index.mjs +1 -2
- package/dist/esm/index.native.js +1 -2
- package/dist/esm/metro-config/getMetroBabelConfigFromViteConfig.mjs +11 -15
- package/dist/esm/metro-config/getMetroBabelConfigFromViteConfig.mjs.map +1 -1
- package/dist/esm/metro-config/getMetroBabelConfigFromViteConfig.native.js +28 -48
- package/dist/esm/metro-config/getMetroBabelConfigFromViteConfig.native.js.map +1 -1
- package/dist/esm/metro-config/getMetroConfigFromViteConfig.mjs +71 -38
- package/dist/esm/metro-config/getMetroConfigFromViteConfig.mjs.map +1 -1
- package/dist/esm/metro-config/getMetroConfigFromViteConfig.native.js +86 -42
- package/dist/esm/metro-config/getMetroConfigFromViteConfig.native.js.map +1 -1
- package/dist/esm/transformer/babel-transformer.mjs +133 -96
- package/dist/esm/transformer/babel-transformer.mjs.map +1 -1
- package/dist/esm/transformer/babel-transformer.native.js +147 -103
- package/dist/esm/transformer/babel-transformer.native.js.map +1 -1
- package/dist/esm/transformer/expoVirtualEnv.mjs +67 -0
- package/dist/esm/transformer/expoVirtualEnv.mjs.map +1 -0
- package/dist/esm/transformer/expoVirtualEnv.native.js +86 -0
- package/dist/esm/transformer/expoVirtualEnv.native.js.map +1 -0
- package/dist/esm/transformer/loadBabelConfig.mjs +1 -1
- package/dist/esm/transformer/loadBabelConfig.mjs.map +1 -1
- package/dist/esm/transformer/loadBabelConfig.native.js +1 -1
- package/dist/esm/transformer/loadBabelConfig.native.js.map +1 -1
- package/dist/esm/transformer/metroNativeWorker.mjs +20 -29
- package/dist/esm/transformer/metroNativeWorker.mjs.map +1 -1
- package/dist/esm/transformer/metroNativeWorker.native.js +24 -30
- package/dist/esm/transformer/metroNativeWorker.native.js.map +1 -1
- package/dist/esm/transformer/transformSync.mjs +3 -3
- package/dist/esm/transformer/transformSync.mjs.map +1 -1
- package/dist/esm/transformer/transformSync.native.js +3 -3
- package/dist/esm/transformer/transformSync.native.js.map +1 -1
- package/dist/esm/utils/exit.mjs +1 -1
- package/dist/esm/utils/exit.mjs.map +1 -1
- package/dist/esm/utils/exit.native.js +1 -1
- package/dist/esm/utils/exit.native.js.map +1 -1
- package/package.json +7 -9
- package/src/babel-plugins/import-meta-env-plugin.ts +1 -1
- package/src/env/platformEnv.test.ts +12 -2
- package/src/env/platformEnv.ts +3 -0
- package/src/index.ts +0 -2
- package/src/metro-config/getMetroBabelConfigFromViteConfig.test.ts +19 -25
- package/src/metro-config/getMetroBabelConfigFromViteConfig.ts +8 -27
- package/src/metro-config/getMetroConfigFromViteConfig.test.ts +60 -0
- package/src/metro-config/getMetroConfigFromViteConfig.ts +119 -96
- package/src/plugins/metroPlugin.ts +3 -6
- package/src/transformer/babel-transformer.test.ts +118 -0
- package/src/transformer/babel-transformer.ts +82 -15
- package/src/transformer/expoVirtualEnv.integration.test.ts +106 -0
- package/src/transformer/expoVirtualEnv.test.ts +72 -0
- package/src/transformer/expoVirtualEnv.ts +115 -0
- package/src/transformer/loadBabelConfig.ts +1 -1
- package/src/transformer/metroNativeWorker.test.ts +134 -17
- package/src/transformer/metroNativeWorker.ts +43 -51
- package/src/transformer/transformSync.test.ts +22 -0
- package/src/transformer/transformSync.ts +4 -4
- package/src/utils/exit.ts +2 -2
- package/src/utils/getTerminalReporter.ts +1 -1
- package/src/utils/projectImport.ts +2 -2
- package/types/babel-plugins/import-meta-env-plugin.d.ts +1 -1
- package/types/env/platformEnv.d.ts +1 -0
- package/types/env/platformEnv.d.ts.map +1 -1
- package/types/index.d.ts +0 -2
- package/types/index.d.ts.map +1 -1
- package/types/metro-config/getMetroBabelConfigFromViteConfig.d.ts +1 -1
- package/types/metro-config/getMetroBabelConfigFromViteConfig.d.ts.map +1 -1
- package/types/metro-config/getMetroConfigFromViteConfig.d.ts +2 -2
- package/types/metro-config/getMetroConfigFromViteConfig.d.ts.map +1 -1
- package/types/plugins/metroPlugin.d.ts +2 -4
- package/types/plugins/metroPlugin.d.ts.map +1 -1
- package/types/transformer/babel-transformer.d.ts +10 -1
- package/types/transformer/babel-transformer.d.ts.map +1 -1
- package/types/transformer/expoVirtualEnv.d.ts +8 -0
- package/types/transformer/expoVirtualEnv.d.ts.map +1 -0
- package/types/transformer/metroNativeWorker.d.ts +3 -5
- package/types/transformer/metroNativeWorker.d.ts.map +1 -1
- package/types/utils/projectImport.d.ts +2 -2
- package/dist/cjs/plugins/expoManifestRequestHandlerPlugin.cjs +0 -59
- package/dist/cjs/plugins/expoManifestRequestHandlerPlugin.native.cjs +0 -62
- package/dist/cjs/plugins/expoManifestRequestHandlerPlugin.native.js +0 -64
- package/dist/cjs/plugins/expoManifestRequestHandlerPlugin.native.js.map +0 -1
- package/dist/cjs/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.cjs +0 -35
- package/dist/cjs/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.native.cjs +0 -37
- package/dist/cjs/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.native.js +0 -39
- package/dist/cjs/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.native.js.map +0 -1
- package/dist/esm/plugins/expoManifestRequestHandlerPlugin.mjs +0 -40
- package/dist/esm/plugins/expoManifestRequestHandlerPlugin.mjs.map +0 -1
- package/dist/esm/plugins/expoManifestRequestHandlerPlugin.native.js +0 -41
- package/dist/esm/plugins/expoManifestRequestHandlerPlugin.native.js.map +0 -1
- package/dist/esm/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.mjs +0 -15
- package/dist/esm/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.mjs.map +0 -1
- package/dist/esm/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.native.js +0 -15
- package/dist/esm/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.native.js.map +0 -1
- package/src/plugins/expoManifestRequestHandlerPlugin.ts +0 -88
- package/src/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.ts +0 -28
- package/types/plugins/expoManifestRequestHandlerPlugin.d.ts +0 -14
- package/types/plugins/expoManifestRequestHandlerPlugin.d.ts.map +0 -1
- package/types/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.d.ts +0 -5
- package/types/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.d.ts.map +0 -1
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterAll, describe, expect, it } from "vitest";
|
|
5
|
+
|
|
6
|
+
const workspaceRoot = path.resolve(__dirname, "../../../..");
|
|
7
|
+
const metroBin = path.join(workspaceRoot, "node_modules/.bin/metro");
|
|
8
|
+
const fixtureDirs: string[] = [];
|
|
9
|
+
|
|
10
|
+
afterAll(() => {
|
|
11
|
+
for (const fixtureDir of fixtureDirs) {
|
|
12
|
+
rmSync(fixtureDir, { recursive: true, force: true });
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe("optional Expo env through One Metro", () => {
|
|
17
|
+
it("executes babel-preset-expo env references in development and production", () => {
|
|
18
|
+
const fixtureDir = mkdtempSync(path.join(workspaceRoot, "tmp-expo-env-"));
|
|
19
|
+
fixtureDirs.push(fixtureDir);
|
|
20
|
+
|
|
21
|
+
writeFileSync(
|
|
22
|
+
path.join(fixtureDir, "package.json"),
|
|
23
|
+
JSON.stringify({ name: "one-expo-env-fixture", private: true }),
|
|
24
|
+
);
|
|
25
|
+
writeFileSync(path.join(fixtureDir, ".watchmanconfig"), "{}\n");
|
|
26
|
+
writeFileSync(
|
|
27
|
+
path.join(fixtureDir, "tsconfig.json"),
|
|
28
|
+
JSON.stringify({ compilerOptions: { baseUrl: ".", paths: {} } }),
|
|
29
|
+
);
|
|
30
|
+
writeFileSync(
|
|
31
|
+
path.join(fixtureDir, "babel.config.cjs"),
|
|
32
|
+
`module.exports = { presets: ['babel-preset-expo'] }\n`,
|
|
33
|
+
);
|
|
34
|
+
writeFileSync(
|
|
35
|
+
path.join(fixtureDir, "metro.config.cjs"),
|
|
36
|
+
`const { withOne } = require('one/metro-config')
|
|
37
|
+
module.exports = withOne(__dirname, { loadViteConfig: false })
|
|
38
|
+
`,
|
|
39
|
+
);
|
|
40
|
+
writeFileSync(
|
|
41
|
+
path.join(fixtureDir, ".env"),
|
|
42
|
+
"EXPO_PUBLIC_SENTINEL=development-dotenv-value\nPRIVATE_SENTINEL=private-value\n",
|
|
43
|
+
);
|
|
44
|
+
writeFileSync(
|
|
45
|
+
path.join(fixtureDir, "index.js"),
|
|
46
|
+
`console.log('observed:' + process.env.EXPO_PUBLIC_SENTINEL)\n`,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const bundle = (dev: boolean, sentinel: string, resetCache = false) => {
|
|
50
|
+
const bundlePath = path.join(
|
|
51
|
+
fixtureDir,
|
|
52
|
+
`bundle.${dev ? "dev" : "prod"}.js`,
|
|
53
|
+
);
|
|
54
|
+
execFileSync(
|
|
55
|
+
metroBin,
|
|
56
|
+
[
|
|
57
|
+
"build",
|
|
58
|
+
"index.js",
|
|
59
|
+
"--config",
|
|
60
|
+
"metro.config.cjs",
|
|
61
|
+
"--platform",
|
|
62
|
+
"ios",
|
|
63
|
+
"--dev",
|
|
64
|
+
String(dev),
|
|
65
|
+
"--minify",
|
|
66
|
+
"false",
|
|
67
|
+
"--out",
|
|
68
|
+
bundlePath,
|
|
69
|
+
...(resetCache ? ["--reset-cache"] : []),
|
|
70
|
+
],
|
|
71
|
+
{
|
|
72
|
+
cwd: fixtureDir,
|
|
73
|
+
encoding: "utf8",
|
|
74
|
+
env: { ...process.env, EXPO_PUBLIC_SENTINEL: sentinel },
|
|
75
|
+
timeout: 180_000,
|
|
76
|
+
},
|
|
77
|
+
);
|
|
78
|
+
return {
|
|
79
|
+
code: readFileSync(bundlePath, "utf8"),
|
|
80
|
+
output: execFileSync(process.execPath, [bundlePath], {
|
|
81
|
+
cwd: fixtureDir,
|
|
82
|
+
encoding: "utf8",
|
|
83
|
+
}),
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const development = bundle(true, "development-process-value", true);
|
|
88
|
+
expect(development.code).toContain("expo/virtual/env.js");
|
|
89
|
+
expect(development.output).toContain("observed:development-dotenv-value");
|
|
90
|
+
expect(development.code).not.toContain("private-value");
|
|
91
|
+
|
|
92
|
+
writeFileSync(
|
|
93
|
+
path.join(fixtureDir, ".env"),
|
|
94
|
+
"EXPO_PUBLIC_SENTINEL=updated-dotenv-value\nPRIVATE_SENTINEL=updated-private-value\n",
|
|
95
|
+
);
|
|
96
|
+
const updatedDevelopment = bundle(true, "updated-process-value");
|
|
97
|
+
expect(updatedDevelopment.output).toContain(
|
|
98
|
+
"observed:updated-dotenv-value",
|
|
99
|
+
);
|
|
100
|
+
expect(updatedDevelopment.code).not.toContain("updated-private-value");
|
|
101
|
+
|
|
102
|
+
const production = bundle(false, "production-process-value");
|
|
103
|
+
expect(production.code).not.toContain("expo/virtual/env.js");
|
|
104
|
+
expect(production.output).toContain("observed:production-process-value");
|
|
105
|
+
}, 180_000);
|
|
106
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { substituteExpoVirtualEnvSource } from './expoVirtualEnv'
|
|
4
|
+
|
|
5
|
+
const virtualFilename = '/proj/node_modules/expo/virtual/env.js'
|
|
6
|
+
const virtualSource = 'export const env = process.env;'
|
|
7
|
+
|
|
8
|
+
describe('substituteExpoVirtualEnvSource', () => {
|
|
9
|
+
it('generates the require.context import in dev', () => {
|
|
10
|
+
const out = substituteExpoVirtualEnvSource({
|
|
11
|
+
filename: virtualFilename,
|
|
12
|
+
src: virtualSource,
|
|
13
|
+
projectRoot: '/proj',
|
|
14
|
+
dev: true,
|
|
15
|
+
environment: 'client',
|
|
16
|
+
})
|
|
17
|
+
expect(out).toContain('require.context("../../..",false,/^\\.\\/\\.env/)')
|
|
18
|
+
expect(out).toContain('.env.development.local')
|
|
19
|
+
expect(out).toContain('...process.env')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('generates the naming-error proxy in production', () => {
|
|
23
|
+
const out = substituteExpoVirtualEnvSource({
|
|
24
|
+
filename: virtualFilename,
|
|
25
|
+
src: virtualSource,
|
|
26
|
+
projectRoot: '/proj',
|
|
27
|
+
dev: false,
|
|
28
|
+
environment: 'client',
|
|
29
|
+
})
|
|
30
|
+
expect(out).toContain('new Proxy')
|
|
31
|
+
expect(out).toContain('not supported in production bundles')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('leaves the virtual module alone outside client environments', () => {
|
|
35
|
+
for (const environment of ['node', 'react-server']) {
|
|
36
|
+
expect(
|
|
37
|
+
substituteExpoVirtualEnvSource({
|
|
38
|
+
filename: virtualFilename,
|
|
39
|
+
src: virtualSource,
|
|
40
|
+
projectRoot: '/proj',
|
|
41
|
+
dev: true,
|
|
42
|
+
environment,
|
|
43
|
+
})
|
|
44
|
+
).toBe(virtualSource)
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('leaves ordinary files alone', () => {
|
|
49
|
+
expect(
|
|
50
|
+
substituteExpoVirtualEnvSource({
|
|
51
|
+
filename: '/proj/src/app.js',
|
|
52
|
+
src: 'const a = 1',
|
|
53
|
+
projectRoot: '/proj',
|
|
54
|
+
dev: true,
|
|
55
|
+
environment: 'client',
|
|
56
|
+
})
|
|
57
|
+
).toBe('const a = 1')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('leaves .env files alone without Expo installed', () => {
|
|
61
|
+
const src = 'EXPO_PUBLIC_A=1\nSECRET=2\n'
|
|
62
|
+
expect(
|
|
63
|
+
substituteExpoVirtualEnvSource({
|
|
64
|
+
filename: '/proj/.env',
|
|
65
|
+
src,
|
|
66
|
+
projectRoot: '/proj-no-expo',
|
|
67
|
+
dev: true,
|
|
68
|
+
environment: 'client',
|
|
69
|
+
})
|
|
70
|
+
).toBe(src)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* narrow optional Expo compatibility shared by the native worker and the
|
|
7
|
+
* babel transformer, mirroring @expo/metro-config's transform worker: only
|
|
8
|
+
* expo/virtual/env.js and .env files are special, and everything else
|
|
9
|
+
* passes through untouched. apps without Expo installed never resolve or
|
|
10
|
+
* load any Expo module: the virtual file cannot match without the expo
|
|
11
|
+
* package, and .env files are left alone when the parser is absent.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
type ParseEnvFile = (src: string, isClient: boolean) => Record<string, string>
|
|
15
|
+
|
|
16
|
+
function loadExpoParseEnvFile(projectRoot: string): ParseEnvFile | null {
|
|
17
|
+
try {
|
|
18
|
+
const requireFromProject = createRequire(path.join(projectRoot, 'package.json'))
|
|
19
|
+
const expoMetroConfigPkg = requireFromProject.resolve(
|
|
20
|
+
'@expo/metro-config/package.json'
|
|
21
|
+
)
|
|
22
|
+
const dotEnvDevelopment = requireFromProject(
|
|
23
|
+
path.join(
|
|
24
|
+
path.dirname(expoMetroConfigPkg),
|
|
25
|
+
'build',
|
|
26
|
+
'transform-worker',
|
|
27
|
+
'dot-env-development'
|
|
28
|
+
)
|
|
29
|
+
) as { parseEnvFile?: ParseEnvFile }
|
|
30
|
+
return dotEnvDevelopment?.parseEnvFile ?? null
|
|
31
|
+
} catch {
|
|
32
|
+
return null
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const virtualEnvPattern = /[\\/]expo[\\/]virtual[\\/]env\.js$/
|
|
37
|
+
const dotEnvPattern = /(^|[\\/])\.env(\.(local|(development|production)(\.local)?))?$/
|
|
38
|
+
const developmentEnvFiles = [
|
|
39
|
+
'.env',
|
|
40
|
+
'.env.development',
|
|
41
|
+
'.env.local',
|
|
42
|
+
'.env.development.local',
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
export function substituteExpoVirtualEnvSource({
|
|
46
|
+
filename,
|
|
47
|
+
src,
|
|
48
|
+
projectRoot,
|
|
49
|
+
dev,
|
|
50
|
+
environment,
|
|
51
|
+
}: {
|
|
52
|
+
filename: string
|
|
53
|
+
src: string
|
|
54
|
+
projectRoot: string
|
|
55
|
+
dev: boolean | undefined
|
|
56
|
+
environment: unknown
|
|
57
|
+
}): string {
|
|
58
|
+
const isClientEnvironment = environment !== 'node' && environment !== 'react-server'
|
|
59
|
+
|
|
60
|
+
// parsing the virtual env is client-only, on the server process.env is used directly.
|
|
61
|
+
if (isClientEnvironment && virtualEnvPattern.test(filename)) {
|
|
62
|
+
if (dev) {
|
|
63
|
+
// variables are merged at runtime so HMR can pick up .env edits.
|
|
64
|
+
// standard Metro's Node crawler does not index `.env` because the file
|
|
65
|
+
// has no extension, so seed the module with the same public-only merge.
|
|
66
|
+
// context modules override this snapshot when the active watcher exposes
|
|
67
|
+
// the files, preserving Expo's live-update path.
|
|
68
|
+
const fileEnv: Record<string, string> = {}
|
|
69
|
+
const parseEnvFile = loadExpoParseEnvFile(projectRoot)
|
|
70
|
+
if (parseEnvFile) {
|
|
71
|
+
for (const file of developmentEnvFiles) {
|
|
72
|
+
try {
|
|
73
|
+
Object.assign(
|
|
74
|
+
fileEnv,
|
|
75
|
+
parseEnvFile(readFileSync(path.join(projectRoot, file), 'utf8'), true)
|
|
76
|
+
)
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (
|
|
79
|
+
typeof error === 'object' &&
|
|
80
|
+
error !== null &&
|
|
81
|
+
'code' in error &&
|
|
82
|
+
error.code === 'ENOENT'
|
|
83
|
+
) {
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
throw error
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const relativePath = path
|
|
91
|
+
.relative(path.dirname(filename), projectRoot)
|
|
92
|
+
.split(path.sep)
|
|
93
|
+
.join('/')
|
|
94
|
+
return `const dotEnvModules = require.context(${JSON.stringify(relativePath)},false,/^\\.\\/\\.env/);\n\nexport const env = { ...process.env, ...${JSON.stringify(fileEnv)}, ...${JSON.stringify(developmentEnvFiles)}.reduce((acc, file) => {\n const key = './' + file;\n return { ...acc, ...(dotEnvModules.keys().includes(key) ? dotEnvModules(key)?.default : {}) };\n}, {}) };`
|
|
95
|
+
}
|
|
96
|
+
// production inlines every value at its use site, so reaching this
|
|
97
|
+
// module at all is a bug worth naming rather than silently undefined.
|
|
98
|
+
return `\nexport const env = new Proxy({}, {\n get(target, key) {\n throw new Error(\`Attempting to access internal environment variable "\${key}" is not supported in production bundles. Environment variables should be inlined in production by Babel.\`);\n },\n});`
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (dotEnvPattern.test(filename)) {
|
|
102
|
+
// parse .env files to objects with Expo's own installed parser, keeping
|
|
103
|
+
// public names only in client environments. without Expo there is
|
|
104
|
+
// nothing to substitute: no preset generates the require.context import
|
|
105
|
+
// that would load a .env module, so the source passes through exactly
|
|
106
|
+
// as it did before this file existed.
|
|
107
|
+
const parseEnvFile = loadExpoParseEnvFile(projectRoot)
|
|
108
|
+
if (!parseEnvFile) {
|
|
109
|
+
return src
|
|
110
|
+
}
|
|
111
|
+
return `export default ${JSON.stringify(parseEnvFile(src, isClientEnvironment))};`
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return src
|
|
115
|
+
}
|
|
@@ -52,7 +52,7 @@ export const loadBabelConfig = (() => {
|
|
|
52
52
|
if (!babelRC.extends) {
|
|
53
53
|
babelRC.presets = [
|
|
54
54
|
// { plugins: [transformImportMetaGlobPlugin] }, // Added to support Vite's `import.meta.glob`
|
|
55
|
-
require('babel-preset
|
|
55
|
+
require('@react-native/babel-preset'),
|
|
56
56
|
]
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -512,7 +512,7 @@ describe('metroNativeWorker', () => {
|
|
|
512
512
|
expect(configDefault.defaultConfig.transformerPath).toContain('metroNativeWorker')
|
|
513
513
|
expect(configDefault.defaultConfig.transformer.transformerPath).toBeUndefined()
|
|
514
514
|
|
|
515
|
-
// Opting back into babel via metroPluginOptions leaves
|
|
515
|
+
// Opting back into babel via metroPluginOptions leaves Metro's default
|
|
516
516
|
// worker in the key, which is itself proof that top level is where metro
|
|
517
517
|
// reads it from.
|
|
518
518
|
const configOptOff = await buildMetroConfigInputFromViteConfig(mockViteConfig, {
|
|
@@ -1518,41 +1518,158 @@ describe('metro babel fallback config names', () => {
|
|
|
1518
1518
|
})
|
|
1519
1519
|
|
|
1520
1520
|
describe('one public env contract in the metro worker', () => {
|
|
1521
|
-
it('inlines
|
|
1521
|
+
it('inlines One and Expo public reads in dev and prod with no virtual module', () => {
|
|
1522
1522
|
for (const isProduction of [false, true]) {
|
|
1523
1523
|
const out = applyInlineEnvVars(
|
|
1524
|
-
|
|
1524
|
+
`export const values = [process.env.ONE_PUBLIC_API, process.env.EXPO_PUBLIC_API, import.meta.env.EXPO_PUBLIC_API];
|
|
1525
|
+
export const all = { ...import.meta.env };`,
|
|
1525
1526
|
'env.ts',
|
|
1526
1527
|
isProduction,
|
|
1527
1528
|
{ ONE_PUBLIC_API: 'https://api.test' }
|
|
1528
1529
|
)
|
|
1529
|
-
expect(out).
|
|
1530
|
+
expect(out).toContain(
|
|
1531
|
+
'export const values = ["https://api.test", "https://api.test", "https://api.test"];'
|
|
1532
|
+
)
|
|
1533
|
+
expect(out).toContain('"EXPO_PUBLIC_API":"https://api.test"')
|
|
1530
1534
|
expect(out).not.toContain('expo/virtual/env')
|
|
1531
1535
|
expect(out).not.toContain('_$$_EXPO_ENV')
|
|
1532
1536
|
}
|
|
1533
1537
|
})
|
|
1534
1538
|
|
|
1535
|
-
it('
|
|
1539
|
+
it('preserves explicit conflicting values', () => {
|
|
1536
1540
|
for (const isProduction of [false, true]) {
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
).toThrow(/rename it to ONE_PUBLIC_\*/)
|
|
1541
|
+
const out = applyInlineEnvVars(
|
|
1542
|
+
'export const values = [process.env.ONE_PUBLIC_API, process.env.EXPO_PUBLIC_API];',
|
|
1543
|
+
'env.ts',
|
|
1544
|
+
isProduction,
|
|
1545
|
+
{ ONE_PUBLIC_API: 'one', EXPO_PUBLIC_API: 'expo' }
|
|
1546
|
+
)
|
|
1547
|
+
expect(out).toBe('export const values = ["one", "expo"];')
|
|
1545
1548
|
}
|
|
1546
1549
|
})
|
|
1547
1550
|
|
|
1548
|
-
it('
|
|
1551
|
+
it('accepts Expo runtime feature flags that are not configured', () => {
|
|
1552
|
+
const out = applyInlineEnvVars(
|
|
1553
|
+
'export const useRnFetch = process.env.EXPO_PUBLIC_USE_RN_FETCH;',
|
|
1554
|
+
'/app/node_modules/expo/build/winter/runtime.native.js',
|
|
1555
|
+
false,
|
|
1556
|
+
{ EXPO_PUBLIC_USE_RN_FETCH: undefined }
|
|
1557
|
+
)
|
|
1558
|
+
expect(out).toBe('export const useRnFetch = undefined;')
|
|
1559
|
+
})
|
|
1560
|
+
|
|
1561
|
+
it('carries public aliases through the whole import.meta.env object', () => {
|
|
1549
1562
|
const out = applyInlineEnvVars(
|
|
1550
1563
|
'export const all = { ...import.meta.env };',
|
|
1551
1564
|
'env.ts',
|
|
1552
1565
|
true,
|
|
1553
|
-
{
|
|
1566
|
+
{ ONE_PUBLIC_API: 'one' }
|
|
1567
|
+
)
|
|
1568
|
+
expect(out).toContain('"ONE_PUBLIC_API":"one"')
|
|
1569
|
+
expect(out).toContain('"EXPO_PUBLIC_API":"one"')
|
|
1570
|
+
})
|
|
1571
|
+
})
|
|
1572
|
+
|
|
1573
|
+
describe('optional expo virtual env', () => {
|
|
1574
|
+
const virtualFilename = '/proj/node_modules/expo/virtual/env.js'
|
|
1575
|
+
const virtualSource = 'export const env = process.env;'
|
|
1576
|
+
|
|
1577
|
+
function runFactory(
|
|
1578
|
+
code: string,
|
|
1579
|
+
opts: { requireImpl: (...args: any[]) => any; procEnv: Record<string, string> }
|
|
1580
|
+
) {
|
|
1581
|
+
const module = { exports: {} as any }
|
|
1582
|
+
vm.runInNewContext(code, {
|
|
1583
|
+
__d(factory: any) {
|
|
1584
|
+
factory(
|
|
1585
|
+
{},
|
|
1586
|
+
opts.requireImpl,
|
|
1587
|
+
(v: any) => v,
|
|
1588
|
+
(v: any) => v,
|
|
1589
|
+
module,
|
|
1590
|
+
module.exports,
|
|
1591
|
+
[]
|
|
1592
|
+
)
|
|
1593
|
+
},
|
|
1594
|
+
process: { env: opts.procEnv },
|
|
1595
|
+
console,
|
|
1596
|
+
})
|
|
1597
|
+
return module.exports
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
const noRequire = () => {
|
|
1601
|
+
throw new Error('unexpected require call')
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
it('merges .env files over process.env in dev like upstream', async () => {
|
|
1605
|
+
const result = await transform({}, '/proj', virtualFilename, Buffer.from(virtualSource), {
|
|
1606
|
+
dev: true,
|
|
1607
|
+
minify: false,
|
|
1608
|
+
platform: 'ios',
|
|
1609
|
+
type: 'module',
|
|
1610
|
+
customTransformOptions: { environment: 'client' },
|
|
1611
|
+
})
|
|
1612
|
+
const code = result.output[0].data.code
|
|
1613
|
+
expect(code).toContain('dotEnvModules')
|
|
1614
|
+
expect(code).toContain('.env.development.local')
|
|
1615
|
+
const contextModule: any = () => ({
|
|
1616
|
+
default: { EXPO_PUBLIC_SENTINEL_DOTENV: 'dotenv-sentinel-value' },
|
|
1617
|
+
})
|
|
1618
|
+
contextModule.keys = () => ['./.env']
|
|
1619
|
+
// metro lowers require.context(rel) to require(contextId, rel)
|
|
1620
|
+
const requireImpl: any = (_id: unknown, _rel?: unknown) => contextModule
|
|
1621
|
+
const mod = runFactory(code, {
|
|
1622
|
+
requireImpl,
|
|
1623
|
+
procEnv: { EXPO_PUBLIC_SENTINEL_PROC: 'proc-sentinel-value' },
|
|
1624
|
+
})
|
|
1625
|
+
expect(mod.env.EXPO_PUBLIC_SENTINEL_DOTENV).toBe('dotenv-sentinel-value')
|
|
1626
|
+
expect(mod.env.EXPO_PUBLIC_SENTINEL_PROC).toBe('proc-sentinel-value')
|
|
1627
|
+
})
|
|
1628
|
+
|
|
1629
|
+
it('throws a naming error in production like upstream', async () => {
|
|
1630
|
+
const result = await transform({}, '/proj', virtualFilename, Buffer.from(virtualSource), {
|
|
1631
|
+
dev: false,
|
|
1632
|
+
minify: false,
|
|
1633
|
+
platform: 'ios',
|
|
1634
|
+
type: 'module',
|
|
1635
|
+
customTransformOptions: { environment: 'client' },
|
|
1636
|
+
})
|
|
1637
|
+
const mod = runFactory(result.output[0].data.code, {
|
|
1638
|
+
requireImpl: noRequire,
|
|
1639
|
+
procEnv: {},
|
|
1640
|
+
})
|
|
1641
|
+
expect(() => mod.env.EXPO_PUBLIC_ANYTHING).toThrow(
|
|
1642
|
+
'not supported in production bundles'
|
|
1643
|
+
)
|
|
1644
|
+
})
|
|
1645
|
+
|
|
1646
|
+
it('leaves .env files alone without Expo installed', async () => {
|
|
1647
|
+
const result = await transform(
|
|
1648
|
+
{},
|
|
1649
|
+
'/proj-no-expo',
|
|
1650
|
+
'/proj-no-expo/.env',
|
|
1651
|
+
Buffer.from('EXPO_PUBLIC_A=1\nSECRET=2\n'),
|
|
1652
|
+
{
|
|
1653
|
+
dev: true,
|
|
1654
|
+
minify: false,
|
|
1655
|
+
platform: 'ios',
|
|
1656
|
+
type: 'module',
|
|
1657
|
+
customTransformOptions: { environment: 'client' },
|
|
1658
|
+
}
|
|
1554
1659
|
)
|
|
1555
|
-
|
|
1556
|
-
expect(
|
|
1660
|
+
const code = result.output[0].data.code
|
|
1661
|
+
expect(code).toContain('EXPO_PUBLIC_A')
|
|
1662
|
+
expect(code).not.toContain('EXPO_PUBLIC_A":"1')
|
|
1663
|
+
})
|
|
1664
|
+
|
|
1665
|
+
it('leaves the virtual module alone outside client environments', async () => {
|
|
1666
|
+
const result = await transform({}, '/proj', virtualFilename, Buffer.from(virtualSource), {
|
|
1667
|
+
dev: true,
|
|
1668
|
+
minify: false,
|
|
1669
|
+
platform: 'ios',
|
|
1670
|
+
type: 'module',
|
|
1671
|
+
customTransformOptions: { environment: 'react-server' },
|
|
1672
|
+
})
|
|
1673
|
+
expect(result.output[0].data.code).not.toContain('require.context')
|
|
1557
1674
|
})
|
|
1558
1675
|
})
|
|
@@ -13,6 +13,9 @@ import {
|
|
|
13
13
|
transformHermesLoops,
|
|
14
14
|
transformReactNativeCodegen,
|
|
15
15
|
} from '@vxrn/compiler'
|
|
16
|
+
import { withExpoPublicEnvAliases } from '@vxrn/utils/publicEnv'
|
|
17
|
+
|
|
18
|
+
import { substituteExpoVirtualEnvSource } from './expoVirtualEnv'
|
|
16
19
|
import { getPlatformEnv, metroPlatformToViteEnvironment } from '../env/platformEnv'
|
|
17
20
|
|
|
18
21
|
/**
|
|
@@ -516,17 +519,15 @@ export function applyModuleResolverAliases(
|
|
|
516
519
|
|
|
517
520
|
/**
|
|
518
521
|
* Native port of one's `babel-plugin-inline-one-server-url` for public env.
|
|
519
|
-
* Every `process.env.ONE_PUBLIC_*` read is
|
|
522
|
+
* Every `process.env.ONE_PUBLIC_*` or `process.env.EXPO_PUBLIC_*` read is
|
|
523
|
+
* inlined as a literal in both modes,
|
|
520
524
|
* matching the rolldown native defines: a native runtime has no `process.env`
|
|
521
525
|
* to read it back out of, and without this the reads survive into the bundle
|
|
522
|
-
* and every
|
|
526
|
+
* and every public value is undefined at runtime.
|
|
523
527
|
*
|
|
524
528
|
* `process.env.ONE_SERVER_URL` is inlined in both modes, matching one's plugin:
|
|
525
529
|
* it is how a native bundle knows where to fetch loader data from.
|
|
526
530
|
*
|
|
527
|
-
* `process.env.EXPO_PUBLIC_*` reads fail with a migration error instead of
|
|
528
|
-
* being copied, ignored, or aliased.
|
|
529
|
-
*
|
|
530
531
|
* Both live in one pass because they are the same rewrite over the same walk,
|
|
531
532
|
* and a second parse of every file is the cost this transformer exists to avoid.
|
|
532
533
|
*/
|
|
@@ -544,6 +545,13 @@ export function applyInlineEnvVars(
|
|
|
544
545
|
}
|
|
545
546
|
if (!parsed?.program) return code
|
|
546
547
|
|
|
548
|
+
const processPublicEnv = Object.fromEntries(
|
|
549
|
+
Object.entries(process.env).filter(
|
|
550
|
+
([key]) => key.startsWith('ONE_PUBLIC_') || key.startsWith('EXPO_PUBLIC_')
|
|
551
|
+
)
|
|
552
|
+
)
|
|
553
|
+
const effectiveEnv = withExpoPublicEnvAliases({ ...processPublicEnv, ...env })
|
|
554
|
+
|
|
547
555
|
const ms = new MagicString(code)
|
|
548
556
|
|
|
549
557
|
// edits are collected rather than written straight through, because a folded
|
|
@@ -597,14 +605,12 @@ export function applyInlineEnvVars(
|
|
|
597
605
|
return
|
|
598
606
|
}
|
|
599
607
|
|
|
600
|
-
if (
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
)
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
if (isProcessEnv && !isAssignmentTarget && key?.startsWith('ONE_PUBLIC_')) {
|
|
607
|
-
replace(node, env[key] ?? process.env[key] ?? undefined)
|
|
608
|
+
if (
|
|
609
|
+
isProcessEnv &&
|
|
610
|
+
!isAssignmentTarget &&
|
|
611
|
+
(key?.startsWith('ONE_PUBLIC_') || key?.startsWith('EXPO_PUBLIC_'))
|
|
612
|
+
) {
|
|
613
|
+
replace(node, effectiveEnv[key] ?? undefined)
|
|
608
614
|
return
|
|
609
615
|
}
|
|
610
616
|
|
|
@@ -617,21 +623,30 @@ export function applyInlineEnvVars(
|
|
|
617
623
|
keyOf(obj.property, obj.computed) === 'env' &&
|
|
618
624
|
key !== undefined
|
|
619
625
|
) {
|
|
620
|
-
replace(node,
|
|
626
|
+
replace(node, effectiveEnv[key])
|
|
621
627
|
return
|
|
622
628
|
}
|
|
623
629
|
|
|
624
630
|
// bare `import.meta.env`, spread or passed around whole.
|
|
625
631
|
if (!isAssignmentTarget && isImportMeta(obj) && key === 'env') {
|
|
626
|
-
edits.push({
|
|
632
|
+
edits.push({
|
|
633
|
+
start: node.start,
|
|
634
|
+
end: node.end,
|
|
635
|
+
text: JSON.stringify(effectiveEnv),
|
|
636
|
+
})
|
|
627
637
|
return
|
|
628
638
|
}
|
|
629
639
|
|
|
630
640
|
// `process.env.X` for anything the vite env map defines. runs after the
|
|
631
|
-
// branches above so ONE_SERVER_URL and
|
|
641
|
+
// branches above so ONE_SERVER_URL and public env keep their own
|
|
632
642
|
// handling.
|
|
633
|
-
if (
|
|
634
|
-
|
|
643
|
+
if (
|
|
644
|
+
isProcessEnv &&
|
|
645
|
+
!isAssignmentTarget &&
|
|
646
|
+
key !== undefined &&
|
|
647
|
+
key in effectiveEnv
|
|
648
|
+
) {
|
|
649
|
+
replace(node, effectiveEnv[key])
|
|
635
650
|
return
|
|
636
651
|
}
|
|
637
652
|
}
|
|
@@ -1883,39 +1898,16 @@ export async function transform(
|
|
|
1883
1898
|
|
|
1884
1899
|
let sourceCode = typeof data === 'string' ? data : data.toString('utf8')
|
|
1885
1900
|
|
|
1886
|
-
//
|
|
1887
|
-
//
|
|
1888
|
-
// without
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
.relative(path.dirname(filename), projectRoot)
|
|
1897
|
-
.split(path.sep)
|
|
1898
|
-
.join('/')
|
|
1899
|
-
sourceCode = `const dotEnvModules = require.context(${JSON.stringify(rel)},false,/^\\.\\/\\.env/);
|
|
1900
|
-
export const env = !dotEnvModules.keys().length ? process.env : { ...process.env, ...['.env', '.env.development', '.env.local', '.env.development.local'].reduce((acc, file) => {
|
|
1901
|
-
return { ...acc, ...(dotEnvModules(file)?.default ?? {}) };
|
|
1902
|
-
}, {}) };`
|
|
1903
|
-
} else {
|
|
1904
|
-
// production inlines every value at its use site, so reaching this module
|
|
1905
|
-
// at all is a bug worth naming rather than silently returning undefined.
|
|
1906
|
-
sourceCode = `export const env = new Proxy({}, {
|
|
1907
|
-
get(target, key) {
|
|
1908
|
-
throw new Error(\`Attempting to access internal environment variable "\${String(key)}" is not supported in production bundles.\`);
|
|
1909
|
-
},
|
|
1910
|
-
});`
|
|
1911
|
-
}
|
|
1912
|
-
} else if (
|
|
1913
|
-
/(^|[\\/])\.env(\.(local|(development|production)(\.local)?))?$/.test(filename)
|
|
1914
|
-
) {
|
|
1915
|
-
const { parseEnvFile } =
|
|
1916
|
-
await import('@expo/metro-config/build/transform-worker/dot-env-development')
|
|
1917
|
-
sourceCode = `export default ${JSON.stringify(parseEnvFile(sourceCode, isClientEnvironment))};`
|
|
1918
|
-
}
|
|
1901
|
+
// narrow optional Expo compatibility, shared with the babel
|
|
1902
|
+
// transformer: only expo/virtual/env.js and .env files are special, and
|
|
1903
|
+
// apps without Expo installed never resolve or load any Expo module.
|
|
1904
|
+
sourceCode = substituteExpoVirtualEnvSource({
|
|
1905
|
+
filename,
|
|
1906
|
+
src: sourceCode,
|
|
1907
|
+
projectRoot,
|
|
1908
|
+
dev: options.dev,
|
|
1909
|
+
environment: options.customTransformOptions?.environment,
|
|
1910
|
+
})
|
|
1919
1911
|
|
|
1920
1912
|
checkReservedStrings(sourceCode, config, options)
|
|
1921
1913
|
|
|
@@ -47,4 +47,26 @@ describe('transformSync', () => {
|
|
|
47
47
|
)
|
|
48
48
|
).toThrow('use `importForwarding.moduleNames`')
|
|
49
49
|
})
|
|
50
|
+
|
|
51
|
+
it('parses the readonly Flow interfaces shipped by React Native 0.87', () => {
|
|
52
|
+
const result = transformSync(
|
|
53
|
+
`// @flow strict
|
|
54
|
+
interface Registration<TArgs> {
|
|
55
|
+
readonly context: unknown;
|
|
56
|
+
readonly listener: (...args: TArgs) => unknown;
|
|
57
|
+
}
|
|
58
|
+
export function readContext(registration: Registration<[]>): unknown {
|
|
59
|
+
return registration.context;
|
|
60
|
+
}`,
|
|
61
|
+
{
|
|
62
|
+
filename: '/project/node_modules/react-native/EventEmitter.js',
|
|
63
|
+
babelrc: false,
|
|
64
|
+
configFile: false,
|
|
65
|
+
presets: [require('@react-native/babel-preset')],
|
|
66
|
+
},
|
|
67
|
+
{ hermesParser: true }
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
expect(result?.code).toContain('function readContext(registration)')
|
|
71
|
+
})
|
|
50
72
|
})
|