Skip to content

Commit 98eaec8

Browse files
committed
feat(#175): @app-config/esbuild
1 parent 9ded2fd commit 98eaec8

File tree

18 files changed

+364
-140
lines changed

18 files changed

+364
-140
lines changed

app-config-esbuild/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('@lcdev/eslint-config/cwd')(__dirname);

app-config-esbuild/README.md

Whitespace-only changes.

app-config-esbuild/package.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "@app-config/esbuild",
3+
"description": "esbuild module resolution support for @app-config",
4+
"version": "2.7.2",
5+
"license": "MPL-2.0",
6+
"author": {
7+
"name": "Launchcode",
8+
"email": "[email protected]",
9+
"url": "https://lc.dev"
10+
},
11+
"repository": {
12+
"type": "git",
13+
"url": "https://github.com/launchcodedev/app-config.git"
14+
},
15+
"main": "dist/index.js",
16+
"module": "dist/es/index.js",
17+
"types": "dist/index.d.ts",
18+
"files": [
19+
"/dist",
20+
"!**/*.tsbuildinfo",
21+
"!**/*.test.*"
22+
],
23+
"scripts": {
24+
"build": "tsc -b",
25+
"build:es": "tsc -b tsconfig.es.json",
26+
"clean": "rm -rf dist *.tsbuildinfo",
27+
"lint": "eslint src",
28+
"fix": "eslint --fix src",
29+
"test": "jest",
30+
"prepublishOnly": "yarn clean && yarn build && yarn build:es"
31+
},
32+
"dependencies": {
33+
"@app-config/config": "^2.7.2",
34+
"@app-config/node": "^2.7.2",
35+
"@app-config/utils": "^2.7.2"
36+
},
37+
"devDependencies": {
38+
"esbuild": "0.13"
39+
},
40+
"prettier": "@lcdev/prettier",
41+
"jest": {
42+
"preset": "@lcdev/jest"
43+
}
44+
}

app-config-esbuild/src/index.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type { Plugin } from 'esbuild';
2+
import { loadValidatedConfig } from '@app-config/config';
3+
import { currentEnvironment } from '@app-config/node';
4+
import { generateModuleText, packageNameRegex } from '@app-config/utils';
5+
6+
const plugin: Plugin = {
7+
name: 'app-config',
8+
setup(build) {
9+
build.onResolve({ filter: packageNameRegex }, (args) => ({
10+
path: args.path,
11+
namespace: 'app-config-ns',
12+
}));
13+
14+
build.onLoad({ filter: /.*/, namespace: 'app-config-ns' }, async () => {
15+
const { fullConfig, validationFunctionCode } = await loadValidatedConfig();
16+
17+
const code = generateModuleText(fullConfig, {
18+
esm: true,
19+
noGlobal: false,
20+
currentEnvironment: currentEnvironment(),
21+
validationFunctionCode,
22+
});
23+
24+
return {
25+
loader: 'js',
26+
contents: code,
27+
};
28+
});
29+
},
30+
};
31+
32+
export default plugin;

app-config-esbuild/tsconfig.es.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"target": "es2019",
5+
"module": "es2020",
6+
"outDir": "./dist/es"
7+
}
8+
}

app-config-esbuild/tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"extends": "@lcdev/tsconfig",
3+
"compilerOptions": {
4+
"rootDir": "./src",
5+
"outDir": "./dist"
6+
},
7+
"include": ["src"],
8+
"exclude": ["node_modules"],
9+
"references": [
10+
{ "path": "../app-config-config" },
11+
{ "path": "../app-config-node" },
12+
{ "path": "../app-config-utils" },
13+
{ "path": "../app-config-test-utils" }
14+
]
15+
}

app-config-rollup/src/index.ts

Lines changed: 17 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Plugin } from 'rollup';
2-
import { packageNameRegex } from '@app-config/utils';
2+
import { generateModuleText, packageNameRegex } from '@app-config/utils';
33
import { ConfigLoadingOptions, loadValidatedConfig } from '@app-config/config';
44
import { asEnvOptions, currentEnvironment } from '@app-config/node';
55
import type { SchemaLoadingOptions } from '@app-config/schema';
@@ -11,8 +11,6 @@ export interface Options {
1111
schemaLoadingOptions?: SchemaLoadingOptions;
1212
}
1313

14-
const privateName = '_appConfig';
15-
1614
// vite resolves first before passing to the rollup plugin
1715
export const appConfigImportRegex = /(app-config|app-config-main)\/dist(\/es)?\/index\.js/;
1816

@@ -36,80 +34,28 @@ export default function appConfigRollup({
3634
},
3735
async load(id) {
3836
if (packageNameRegex.exec(id) || appConfigImportRegex.exec(id)) {
39-
const {
40-
parsed: config,
41-
validationFunctionCode,
42-
filePaths,
43-
} = await loadValidatedConfig(loadingOptions, schemaLoadingOptions);
37+
const { fullConfig, validationFunctionCode, filePaths } = await loadValidatedConfig(
38+
loadingOptions,
39+
schemaLoadingOptions,
40+
);
4441

4542
if (filePaths) {
4643
currentFilePaths.length = 0;
4744
currentFilePaths.push(...filePaths);
4845
}
4946

50-
let generatedText: string;
51-
52-
if (readGlobal) {
53-
generatedText = `
54-
const configValue = ${JSON.stringify(config)};
55-
56-
const globalNamespace = (typeof window === 'undefined' ? globalThis : window) || {};
57-
58-
// if the global was already defined, use it
59-
const config = (globalNamespace.${privateName} || configValue);
60-
61-
// if the global is frozen then it was set by electron and we can't change it, but we'll set it if we can
62-
if (
63-
typeof globalNamespace.${privateName} === 'undefined' ||
64-
!Object.isFrozen(globalNamespace.${privateName})
65-
) {
66-
globalNamespace.${privateName} = config;
67-
}
68-
`;
69-
} else {
70-
generatedText = `
71-
const config = ${JSON.stringify(config)};
72-
`;
73-
}
74-
75-
if (injectValidationFunction && validationFunctionCode) {
76-
const [code, imports] = validationFunctionCode(true);
77-
78-
generatedText = `${generatedText}
79-
${imports}
80-
81-
${/* nest the generated commonjs module here */ ''}
82-
function genValidateConfig(){
83-
const validateConfigModule = {};
84-
(function(module){${code}})(validateConfigModule);
85-
return validateConfigModule.exports;
86-
}
87-
88-
${/* marking as pure allows tree shaking */ ''}
89-
export const validateConfig = /*#__PURE__*/ genValidateConfig();
90-
`;
91-
}
92-
93-
return `
94-
${generatedText}
95-
96-
export { config };
97-
export default config;
98-
99-
export function currentEnvironment() {
100-
return ${
101-
JSON.stringify(
102-
currentEnvironment(
103-
asEnvOptions(
104-
loadingOptions?.environmentOverride,
105-
loadingOptions?.environmentAliases,
106-
loadingOptions?.environmentSourceNames,
107-
),
108-
),
109-
) ?? 'undefined'
110-
};
111-
}
112-
`;
47+
return generateModuleText(fullConfig, {
48+
esm: true,
49+
noGlobal: !readGlobal,
50+
currentEnvironment: currentEnvironment(
51+
asEnvOptions(
52+
loadingOptions?.environmentOverride,
53+
loadingOptions?.environmentAliases,
54+
loadingOptions?.environmentSourceNames,
55+
),
56+
),
57+
validationFunctionCode: injectValidationFunction ? validationFunctionCode : undefined,
58+
});
11359
}
11460

11561
return null;

app-config-utils/src/index.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,95 @@ export function isObject(obj: Json): obj is JsonObject {
2424
export function isPrimitive(obj: Json): obj is JsonPrimitive {
2525
return !isObject(obj) && !Array.isArray(obj);
2626
}
27+
28+
export function generateModuleText(
29+
fullConfig: Json,
30+
{
31+
esm,
32+
noGlobal,
33+
currentEnvironment,
34+
validationFunctionCode,
35+
}: {
36+
esm: boolean;
37+
noGlobal: boolean;
38+
currentEnvironment: string | undefined;
39+
validationFunctionCode?(): string;
40+
validationFunctionCode?(esm: true): [string, string];
41+
},
42+
): string {
43+
const privateName = '_appConfig';
44+
const config = JSON.stringify(fullConfig);
45+
46+
let generatedText: string;
47+
48+
if (noGlobal) {
49+
generatedText = `
50+
const config = ${config};
51+
52+
export { config };
53+
export default config;
54+
`;
55+
} else {
56+
generatedText = `
57+
const configValue = ${config};
58+
59+
const globalNamespace = (typeof window === 'undefined' ? globalThis : window) || {};
60+
61+
// if the global was already defined, use it
62+
const config = (globalNamespace.${privateName} || configValue);
63+
64+
// if the global is frozen then it was set by electron and we can't change it, but we'll set it if we can
65+
if (
66+
typeof globalNamespace.${privateName} === 'undefined' ||
67+
!Object.isFrozen(globalNamespace.${privateName})
68+
) {
69+
globalNamespace.${privateName} = config;
70+
}
71+
72+
export { config };
73+
export default config;
74+
`;
75+
}
76+
77+
if (validationFunctionCode) {
78+
if (esm) {
79+
const [code, imports] = validationFunctionCode(true);
80+
81+
generatedText = `${generatedText}
82+
${imports}
83+
84+
${/* nest the generated commonjs module here */ ''}
85+
function genValidateConfig(){
86+
const validateConfigModule = {};
87+
(function(module){${code}})(validateConfigModule);
88+
return validateConfigModule.exports;
89+
}
90+
91+
${/* marking as pure allows tree shaking */ ''}
92+
export const validateConfig = /*#__PURE__*/ genValidateConfig();
93+
`;
94+
} else {
95+
const code = validationFunctionCode();
96+
97+
generatedText = `${generatedText}
98+
${/* nest the generated commonjs module here */ ''}
99+
function genValidateConfig(){
100+
const validateConfigModule = {};
101+
(function(module){${code}})(validateConfigModule);
102+
return validateConfigModule.exports;
103+
}
104+
105+
${/* marking as pure always allows tree shaking in webpack when using es modules */ ''}
106+
export const validateConfig = /*#__PURE__*/ genValidateConfig();
107+
`;
108+
}
109+
}
110+
111+
generatedText = `${generatedText}
112+
export function currentEnvironment() {
113+
return ${currentEnvironment ? JSON.stringify(currentEnvironment) : 'undefined'};
114+
}
115+
`;
116+
117+
return generatedText;
118+
}

app-config-webpack/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface Options {
1313
loading?: ConfigLoadingOptions;
1414
schemaLoading?: SchemaLoadingOptions;
1515
intercept?: RegExp;
16+
injectValidationFunction?: boolean;
1617
}
1718

1819
export default class AppConfigPlugin {

0 commit comments

Comments
 (0)