Skip to content

Commit 286bb5a

Browse files
committed
New: Add option to place values of env vars or static values to the package.json
1 parent 4a5b245 commit 286bb5a

9 files changed

Lines changed: 178 additions & 5 deletions

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,21 @@ For example you can use ESM with the following setup:
6565
}
6666
```
6767

68+
Example of using custom package.json values:
69+
70+
```json
71+
{
72+
"settings": {
73+
"customPackageFields": {
74+
"repository": {
75+
"type": "git",
76+
"url": "${YOUR_ENV_VARIABLE}"
77+
}
78+
}
79+
}
80+
}
81+
```
82+
6883
## Recommendations
6984

7085
When used together with TypeScript, [esbuild will not perform type checking](https://esbuild.github.io/content-types/#typescript), so it's recommended to run `tsc --noEmit` before using `funpack`.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
"scripts": {
1616
"precompile": "rimraf ./dist",
1717
"compile": "tsc",
18-
"test": "yarn jest --coverage && yarn lint && yarn prettier",
19-
"fix": "yarn lint --fix && yarn prettier-fix",
18+
"test": "npm run jest --coverage && npm run lint && npm run prettier",
19+
"fix": "npm run lint --fix && npm run prettier-fix",
2020
"jest": "jest",
21-
"lint": "eslint .",
21+
"lint": "eslint ./src",
2222
"prettier": "prettier --check ./src",
2323
"prettier-fix": "prettier --write ./src"
2424
},

src/index.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { join } from 'path';
22

33
import funpack from './index';
44

5+
process.env.GIT_REPO_URL = 'https://example.com/test/repo.git';
6+
57
jest.mock('./utils/getPackageJsonObject', () =>
68
jest.fn().mockReturnValue({
79
name: 'my-lambdas',
@@ -14,6 +16,12 @@ jest.mock('./utils/getPackageJsonObject', () =>
1416
cleanupOutputDir: true,
1517
zip: true,
1618
removeDirAfterZip: true,
19+
customPackageFields: {
20+
repository: {
21+
type: 'git',
22+
url: '${GIT_REPO_URL}',
23+
},
24+
},
1725
},
1826
functions: {
1927
testfunc: './example/test.ts',
@@ -31,13 +39,13 @@ describe('funpack', () => {
3139
expect(console.log).toBeCalledWith(
3240
join('example', 'dist', 'testfunc.zip'),
3341
'-',
34-
20728,
42+
20771,
3543
'total bytes'
3644
);
3745
expect(console.log).toBeCalledWith(
3846
join('example', 'dist', 'second.zip'),
3947
'-',
40-
20725,
48+
20768,
4149
'total bytes'
4250
);
4351
});

src/parts/parseConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const configSchema = z.object(
1919
zip: z.boolean().default(false),
2020
// Remove directories of functions, leaving only the zip files
2121
removeDirAfterZip: z.boolean().default(false),
22+
// Custom values to append to the packge.json
23+
customPackageFields: z.record(z.string(), z.any()).default({}),
2224
}),
2325
},
2426
{ required_error: 'Missing funpack config inside package.json!' }

src/utils/envVarReplace.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import envVarReplace from './envVarReplace';
2+
3+
describe('envVarStringReplace', () => {
4+
process.env.TEST_EXAMPLE = 'test';
5+
process.env.SECOND_EXAMPLE = 'example1';
6+
it('returns correctly for a string with just a variable', () => {
7+
const value = envVarReplace('${TEST_EXAMPLE}');
8+
expect(value).toBe('test');
9+
});
10+
11+
it('returns correctly for a string with a variable and custom text', () => {
12+
const value = envVarReplace('${TEST_EXAMPLE}-example');
13+
expect(value).toBe('test-example');
14+
});
15+
16+
it('returns correctly for a string with two variables', () => {
17+
const value = envVarReplace('${TEST_EXAMPLE}-${SECOND_EXAMPLE}');
18+
expect(value).toBe('test-example1');
19+
});
20+
});

src/utils/envVarReplace.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const envVarReplace = (value: string) => {
2+
let newValue = value;
3+
for (let i = 0; i < value.length; i++) {
4+
if (value.substring(i, i + 2) === '${') {
5+
const stopIndex = value.indexOf('}', i);
6+
if (stopIndex !== -1) {
7+
const envVarName = value.substring(i + 2, stopIndex);
8+
const envVarValue = process.env[envVarName];
9+
const regex = new RegExp(`\\\${${envVarName}}`, 'g');
10+
newValue = newValue.replace(regex, envVarValue || '');
11+
}
12+
}
13+
}
14+
return newValue;
15+
};
16+
17+
export default envVarReplace;

src/utils/prepareFunctionPackageJson.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { readFileSync } from 'fs';
22
import { join } from 'path';
33

44
import type { PackageObjectType } from '../types';
5+
import traverseAndAction from './traverseAndAction';
6+
import envVarReplace from './envVarReplace';
57

68
interface ParamsType {
79
packages: string[];
@@ -14,6 +16,7 @@ const prepareFunctionPackageJson = ({
1416
packageObject,
1517
mainPath,
1618
}: ParamsType): Record<string, unknown> => {
19+
// Get used dependencies and their versions
1720
const dependencies: Record<string, string> = {};
1821
for (const packageName of packages) {
1922
const packageJsonPath = join('node_modules', packageName, 'package.json');
@@ -24,14 +27,24 @@ const prepareFunctionPackageJson = ({
2427
dependencies[packageName] = version;
2528
}
2629

30+
// Copy fields from root package.json
2731
const fieldsToCopy = packageObject.funpack.settings.packageFieldsToCopy;
2832
const copiedFromMainPackage: Record<string, unknown> = {};
2933
for (const fieldName of fieldsToCopy) {
3034
copiedFromMainPackage[fieldName] = packageObject[fieldName];
3135
}
3236

37+
// Set specific vars or static strings to specified package.json fields
38+
const customPackageFields =
39+
packageObject.funpack.settings.customPackageFields;
40+
const customObject = traverseAndAction(
41+
customPackageFields,
42+
envVarReplace
43+
) as typeof customPackageFields;
44+
3345
return {
3446
...copiedFromMainPackage,
47+
...customObject,
3548
main: mainPath,
3649
dependencies,
3750
};
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import traverseAndAction from './traverseAndAction';
2+
import envVarReplace from './envVarReplace';
3+
4+
describe('traverseAndAction', () => {
5+
it('returns correctly updated object', () => {
6+
const action = (value: string) => value.replace('a', 'e');
7+
const newObject = traverseAndAction(
8+
{
9+
test: {
10+
example: 'aaaa',
11+
},
12+
test2: 'bba',
13+
},
14+
action
15+
);
16+
expect(newObject).toEqual({
17+
test: {
18+
example: 'eaaa',
19+
},
20+
test2: 'bbe',
21+
});
22+
});
23+
24+
it('returns correcly when using env var replace', () => {
25+
process.env.EXAMPLE = 'test1';
26+
const newObject = traverseAndAction(
27+
{
28+
test: {
29+
example: '${EXAMPLE}',
30+
},
31+
test2: 'test:${EXAMPLE}',
32+
},
33+
envVarReplace
34+
);
35+
expect(newObject).toEqual({
36+
test: {
37+
example: 'test1',
38+
},
39+
test2: 'test:test1',
40+
});
41+
});
42+
43+
it('returns correcly when using arrays and numbers', () => {
44+
process.env.EXAMPLE = 'test1';
45+
const newObject = traverseAndAction(
46+
{
47+
number: 1,
48+
array: ['test', '${EXAMPLE}'],
49+
arrayWithObjects: [{ test1: 'example', test2: '${EXAMPLE}' }],
50+
arrayWithArray: [[{ test: 'test' }, '${EXAMPLE}'], 'test'],
51+
},
52+
envVarReplace
53+
);
54+
expect(newObject).toEqual({
55+
number: 1,
56+
array: ['test', 'test1'],
57+
arrayWithObjects: [{ test1: 'example', test2: 'test1' }],
58+
arrayWithArray: [[{ test: 'test' }, 'test1'], 'test'],
59+
});
60+
});
61+
});

src/utils/traverseAndAction.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
type ValueType = string | object | number | any[] | null;
2+
type ObjectType = Record<string, ValueType>;
3+
4+
const isObject = (value: unknown): boolean => {
5+
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
6+
return true;
7+
}
8+
return false;
9+
};
10+
11+
const traverseAndAction = (
12+
input: ValueType,
13+
actionOnString: (value: string) => string
14+
): ValueType => {
15+
if (typeof input === 'string') {
16+
return actionOnString(input);
17+
} else if (typeof input === 'number') {
18+
return input;
19+
} else if (Array.isArray(input)) {
20+
return input.map((arrayValue) =>
21+
traverseAndAction(arrayValue, actionOnString)
22+
);
23+
} else if (isObject(input)) {
24+
const newObject: ObjectType = {};
25+
for (const key in input) {
26+
if (Object.prototype.hasOwnProperty.call(input, key)) {
27+
const element = (input as ObjectType)[key];
28+
newObject[key] = traverseAndAction(element, actionOnString);
29+
}
30+
}
31+
return newObject;
32+
}
33+
34+
return null;
35+
};
36+
37+
export default traverseAndAction;

0 commit comments

Comments
 (0)