@lowdefy/build 4.6.0 → 4.7.0
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/build/buildRefs/buildRefs.js +35 -19
- package/dist/build/buildRefs/evaluateStaticOperators.js +5 -9
- package/dist/build/buildRefs/parseRefContent.js +8 -1
- package/dist/build/buildRefs/walker.js +340 -0
- package/dist/build/jit/buildPageJit.js +48 -29
- package/dist/build/jit/createPageRegistry.js +6 -1
- package/dist/build/jit/shallowBuild.js +1 -4
- package/dist/defaultTypesMap.js +338 -338
- package/dist/index.js +8 -6
- package/dist/indexDev.js +1 -0
- package/dist/lowdefySchema.js +0 -1
- package/dist/utils/makeId.js +3 -0
- package/package.json +42 -42
- package/dist/build/buildRefs/createRefReviver.js +0 -28
- package/dist/build/buildRefs/evaluateBuildOperators.js +0 -53
- package/dist/build/buildRefs/getRefsFromFile.js +0 -42
- package/dist/build/buildRefs/populateRefs.js +0 -105
- package/dist/build/buildRefs/recursiveBuild.js +0 -133
- package/dist/build/jit/getRefPositions.js +0 -38
- package/dist/build/jit/stripPageContent.js +0 -23
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import { BuildError, LowdefyInternalError } from '@lowdefy/errors';
|
|
15
|
+
*/ import { BuildError, ConfigError, LowdefyInternalError } from '@lowdefy/errors';
|
|
16
16
|
import createContext from './createContext.js';
|
|
17
17
|
import createPluginTypesMap from './utils/createPluginTypesMap.js';
|
|
18
18
|
import logCollectedErrors from './utils/logCollectedErrors.js';
|
|
@@ -62,13 +62,15 @@ async function build(options) {
|
|
|
62
62
|
context
|
|
63
63
|
});
|
|
64
64
|
} catch (err) {
|
|
65
|
-
//
|
|
66
|
-
if (err
|
|
67
|
-
context.
|
|
68
|
-
|
|
65
|
+
// Root lowdefy.yaml failure still throws from buildRefs — collect it
|
|
66
|
+
if (err instanceof ConfigError) {
|
|
67
|
+
context.errors.push(err);
|
|
68
|
+
} else {
|
|
69
|
+
throw err;
|
|
69
70
|
}
|
|
70
|
-
throw err;
|
|
71
71
|
}
|
|
72
|
+
// Stop if buildRefs collected any errors (YAML parse, missing files, etc.)
|
|
73
|
+
logCollectedErrors(context);
|
|
72
74
|
// Build steps - collect all errors before stopping
|
|
73
75
|
// addKeys runs first so testSchema has ~k markers for error location info
|
|
74
76
|
tryBuildStep(addKeys, 'addKeys', {
|
package/dist/indexDev.js
CHANGED
|
@@ -16,3 +16,4 @@
|
|
|
16
16
|
export { default as buildPageJit } from './build/jit/buildPageJit.js';
|
|
17
17
|
export { default as createPageRegistry } from './build/jit/createPageRegistry.js';
|
|
18
18
|
export { default as createContext } from './createContext.js';
|
|
19
|
+
export { default as makeId } from './utils/makeId.js';
|
package/dist/lowdefySchema.js
CHANGED
package/dist/utils/makeId.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lowdefy/build",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"homepage": "https://lowdefy.com",
|
|
@@ -45,15 +45,15 @@
|
|
|
45
45
|
"dist/*"
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@lowdefy/ajv": "4.
|
|
49
|
-
"@lowdefy/blocks-basic": "4.
|
|
50
|
-
"@lowdefy/blocks-loaders": "4.
|
|
51
|
-
"@lowdefy/errors": "4.
|
|
52
|
-
"@lowdefy/helpers": "4.
|
|
53
|
-
"@lowdefy/node-utils": "4.
|
|
54
|
-
"@lowdefy/nunjucks": "4.
|
|
55
|
-
"@lowdefy/operators": "4.
|
|
56
|
-
"@lowdefy/operators-js": "4.
|
|
48
|
+
"@lowdefy/ajv": "4.7.0",
|
|
49
|
+
"@lowdefy/blocks-basic": "4.7.0",
|
|
50
|
+
"@lowdefy/blocks-loaders": "4.7.0",
|
|
51
|
+
"@lowdefy/errors": "4.7.0",
|
|
52
|
+
"@lowdefy/helpers": "4.7.0",
|
|
53
|
+
"@lowdefy/node-utils": "4.7.0",
|
|
54
|
+
"@lowdefy/nunjucks": "4.7.0",
|
|
55
|
+
"@lowdefy/operators": "4.7.0",
|
|
56
|
+
"@lowdefy/operators-js": "4.7.0",
|
|
57
57
|
"ajv": "8.12.0",
|
|
58
58
|
"json5": "2.2.3",
|
|
59
59
|
"yaml": "2.3.4",
|
|
@@ -61,41 +61,41 @@
|
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@jest/globals": "28.1.3",
|
|
64
|
-
"@lowdefy/actions-core": "4.
|
|
65
|
-
"@lowdefy/actions-pdf-make": "4.
|
|
66
|
-
"@lowdefy/blocks-aggrid": "4.
|
|
67
|
-
"@lowdefy/blocks-algolia": "4.
|
|
68
|
-
"@lowdefy/blocks-antd": "4.
|
|
69
|
-
"@lowdefy/blocks-color-selectors": "4.
|
|
70
|
-
"@lowdefy/blocks-echarts": "4.
|
|
71
|
-
"@lowdefy/blocks-google-maps": "4.
|
|
72
|
-
"@lowdefy/blocks-markdown": "4.
|
|
73
|
-
"@lowdefy/blocks-qr": "4.
|
|
74
|
-
"@lowdefy/connection-axios-http": "4.
|
|
75
|
-
"@lowdefy/connection-elasticsearch": "4.
|
|
76
|
-
"@lowdefy/connection-test": "4.
|
|
77
|
-
"@lowdefy/connection-google-sheets": "4.
|
|
78
|
-
"@lowdefy/connection-knex": "4.
|
|
79
|
-
"@lowdefy/connection-mongodb": "4.
|
|
80
|
-
"@lowdefy/connection-redis": "4.
|
|
81
|
-
"@lowdefy/connection-sendgrid": "4.
|
|
82
|
-
"@lowdefy/connection-stripe": "4.
|
|
83
|
-
"@lowdefy/operators-change-case": "4.
|
|
84
|
-
"@lowdefy/operators-diff": "4.
|
|
85
|
-
"@lowdefy/operators-jsonata": "4.
|
|
86
|
-
"@lowdefy/operators-moment": "4.
|
|
87
|
-
"@lowdefy/operators-mql": "4.
|
|
88
|
-
"@lowdefy/operators-nunjucks": "4.
|
|
89
|
-
"@lowdefy/operators-uuid": "4.
|
|
90
|
-
"@lowdefy/operators-yaml": "4.
|
|
91
|
-
"@lowdefy/plugin-auth0": "4.
|
|
92
|
-
"@lowdefy/plugin-aws": "4.
|
|
93
|
-
"@lowdefy/plugin-csv": "4.
|
|
94
|
-
"@lowdefy/plugin-next-auth": "4.
|
|
64
|
+
"@lowdefy/actions-core": "4.7.0",
|
|
65
|
+
"@lowdefy/actions-pdf-make": "4.7.0",
|
|
66
|
+
"@lowdefy/blocks-aggrid": "4.7.0",
|
|
67
|
+
"@lowdefy/blocks-algolia": "4.7.0",
|
|
68
|
+
"@lowdefy/blocks-antd": "4.7.0",
|
|
69
|
+
"@lowdefy/blocks-color-selectors": "4.7.0",
|
|
70
|
+
"@lowdefy/blocks-echarts": "4.7.0",
|
|
71
|
+
"@lowdefy/blocks-google-maps": "4.7.0",
|
|
72
|
+
"@lowdefy/blocks-markdown": "4.7.0",
|
|
73
|
+
"@lowdefy/blocks-qr": "4.7.0",
|
|
74
|
+
"@lowdefy/connection-axios-http": "4.7.0",
|
|
75
|
+
"@lowdefy/connection-elasticsearch": "4.7.0",
|
|
76
|
+
"@lowdefy/connection-test": "4.7.0",
|
|
77
|
+
"@lowdefy/connection-google-sheets": "4.7.0",
|
|
78
|
+
"@lowdefy/connection-knex": "4.7.0",
|
|
79
|
+
"@lowdefy/connection-mongodb": "4.7.0",
|
|
80
|
+
"@lowdefy/connection-redis": "4.7.0",
|
|
81
|
+
"@lowdefy/connection-sendgrid": "4.7.0",
|
|
82
|
+
"@lowdefy/connection-stripe": "4.7.0",
|
|
83
|
+
"@lowdefy/operators-change-case": "4.7.0",
|
|
84
|
+
"@lowdefy/operators-diff": "4.7.0",
|
|
85
|
+
"@lowdefy/operators-jsonata": "4.7.0",
|
|
86
|
+
"@lowdefy/operators-moment": "4.7.0",
|
|
87
|
+
"@lowdefy/operators-mql": "4.7.0",
|
|
88
|
+
"@lowdefy/operators-nunjucks": "4.7.0",
|
|
89
|
+
"@lowdefy/operators-uuid": "4.7.0",
|
|
90
|
+
"@lowdefy/operators-yaml": "4.7.0",
|
|
91
|
+
"@lowdefy/plugin-auth0": "4.7.0",
|
|
92
|
+
"@lowdefy/plugin-aws": "4.7.0",
|
|
93
|
+
"@lowdefy/plugin-csv": "4.7.0",
|
|
94
|
+
"@lowdefy/plugin-next-auth": "4.7.0",
|
|
95
95
|
"@swc/cli": "0.1.63",
|
|
96
96
|
"@swc/core": "1.3.99",
|
|
97
97
|
"@swc/jest": "0.2.29",
|
|
98
|
-
"@lowdefy/logger": "4.
|
|
98
|
+
"@lowdefy/logger": "4.7.0",
|
|
99
99
|
"jest": "28.1.3",
|
|
100
100
|
"pino": "8.16.2"
|
|
101
101
|
},
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ import { type } from '@lowdefy/helpers';
|
|
16
|
-
import setNonEnumerableProperty from '../../utils/setNonEnumerableProperty.js';
|
|
17
|
-
// Returns a serializer.copy reviver that sets ~r on all objects
|
|
18
|
-
// that don't already have it, preserving original file references.
|
|
19
|
-
function createRefReviver(refId) {
|
|
20
|
-
return (_, value)=>{
|
|
21
|
-
if (!type.isObject(value) && !type.isArray(value)) return value;
|
|
22
|
-
if (value['~r'] === undefined) {
|
|
23
|
-
setNonEnumerableProperty(value, '~r', refId);
|
|
24
|
-
}
|
|
25
|
-
return value;
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
export default createRefReviver;
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ import { BuildParser } from '@lowdefy/operators';
|
|
16
|
-
import operators from '@lowdefy/operators-js/operators/build';
|
|
17
|
-
import collectDynamicIdentifiers from '../collectDynamicIdentifiers.js';
|
|
18
|
-
import validateOperatorsDynamic from '../validateOperatorsDynamic.js';
|
|
19
|
-
import collectExceptions from '../../utils/collectExceptions.js';
|
|
20
|
-
// Validate and collect dynamic identifiers once at module load
|
|
21
|
-
validateOperatorsDynamic({
|
|
22
|
-
operators
|
|
23
|
-
});
|
|
24
|
-
const dynamicIdentifiers = collectDynamicIdentifiers({
|
|
25
|
-
operators
|
|
26
|
-
});
|
|
27
|
-
function evaluateBuildOperators({ context, input, refDef, typeNames }) {
|
|
28
|
-
const operatorsParser = new BuildParser({
|
|
29
|
-
env: process.env,
|
|
30
|
-
operators,
|
|
31
|
-
dynamicIdentifiers,
|
|
32
|
-
...typeNames ? {
|
|
33
|
-
typeNames
|
|
34
|
-
} : {}
|
|
35
|
-
});
|
|
36
|
-
const { output, errors } = operatorsParser.parse({
|
|
37
|
-
input,
|
|
38
|
-
operatorPrefix: '_build.'
|
|
39
|
-
});
|
|
40
|
-
if (errors.length > 0) {
|
|
41
|
-
errors.forEach((error)=>{
|
|
42
|
-
// Resolve source file path for error location.
|
|
43
|
-
// Two call sites: (1) recursiveBuild per-file where refDef.path is correct
|
|
44
|
-
// but ~r isn't set yet (createRefReviver runs after), and (2) buildRefs
|
|
45
|
-
// top-level where refDef is root lowdefy.yaml but ~r identifies the real
|
|
46
|
-
// source file via refMap. The fallback to refDef.path handles case (1).
|
|
47
|
-
error.filePath = error.refId ? context.refMap[error.refId]?.path : refDef.path;
|
|
48
|
-
collectExceptions(context, error);
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
return output;
|
|
52
|
-
}
|
|
53
|
-
export default evaluateBuildOperators;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ import { serializer, type } from '@lowdefy/helpers';
|
|
16
|
-
import makeRefDefinition from './makeRefDefinition.js';
|
|
17
|
-
function getRefsFromFile(fileContent, parentRefDefId, refMap) {
|
|
18
|
-
const foundRefs = [];
|
|
19
|
-
const reviver = (key, value)=>{
|
|
20
|
-
if (type.isObject(value)) {
|
|
21
|
-
if (!type.isUndefined(value._ref)) {
|
|
22
|
-
// Capture line number from the object containing the _ref
|
|
23
|
-
const lineNumber = value['~l'];
|
|
24
|
-
const def = makeRefDefinition(value._ref, parentRefDefId, refMap, lineNumber);
|
|
25
|
-
foundRefs.push(def);
|
|
26
|
-
return {
|
|
27
|
-
_ref: def
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return value;
|
|
32
|
-
};
|
|
33
|
-
// Use serializer.copy to preserve non-enumerable properties like ~l
|
|
34
|
-
const fileContentBuiltRefs = serializer.copy(fileContent, {
|
|
35
|
-
reviver
|
|
36
|
-
});
|
|
37
|
-
return {
|
|
38
|
-
foundRefs,
|
|
39
|
-
fileContentBuiltRefs
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
export default getRefsFromFile;
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ import { get, serializer, type } from '@lowdefy/helpers';
|
|
16
|
-
/**
|
|
17
|
-
* Copies a _var value while preserving source location markers.
|
|
18
|
-
*
|
|
19
|
-
* When _var copies a value from vars into a template, we need to preserve
|
|
20
|
-
* the ~r (ref ID) and ~l (line number) from WHERE THE VALUE IS DEFINED
|
|
21
|
-
* (the source file), not where _var is used (the template).
|
|
22
|
-
*
|
|
23
|
-
* The issue is that serializer.copy loses non-enumerable properties, and
|
|
24
|
-
* recursiveBuild.js then sets ~r to the template file for objects without ~r.
|
|
25
|
-
*
|
|
26
|
-
* This function preserves the source location by making ~r enumerable on
|
|
27
|
-
* the copied value, so it survives through subsequent serializer.copy calls.
|
|
28
|
-
*
|
|
29
|
-
* @param {*} value - The value to copy from vars
|
|
30
|
-
* @param {string} sourceRefId - The ref ID of the source file where the var is defined
|
|
31
|
-
* @returns {*} The copied value with preserved source location markers
|
|
32
|
-
*/ function copyVarValue(value, sourceRefId) {
|
|
33
|
-
if (!type.isObject(value) && !type.isArray(value)) {
|
|
34
|
-
return value;
|
|
35
|
-
}
|
|
36
|
-
// Copy the value, preserving ~l and setting ~r to the source file
|
|
37
|
-
return serializer.copy(value, {
|
|
38
|
-
reviver: (_, v)=>{
|
|
39
|
-
if (type.isObject(v) || type.isArray(v)) {
|
|
40
|
-
// Preserve the source file's ref ID by setting it explicitly
|
|
41
|
-
// This prevents recursiveBuild from overwriting it with the template's ref ID
|
|
42
|
-
if (sourceRefId && v['~r'] === undefined) {
|
|
43
|
-
v['~r'] = sourceRefId;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return v;
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
function refReviver(key, value) {
|
|
51
|
-
if (type.isObject(value)) {
|
|
52
|
-
if (!type.isUndefined(value._ref)) {
|
|
53
|
-
const result = this.parsedFiles[value._ref.id];
|
|
54
|
-
if (value._ref.ignoreBuildChecks !== undefined) {
|
|
55
|
-
if (type.isObject(result)) {
|
|
56
|
-
result['~ignoreBuildChecks'] = value._ref.ignoreBuildChecks;
|
|
57
|
-
} else if (type.isArray(result)) {
|
|
58
|
-
result.forEach((item)=>{
|
|
59
|
-
if (type.isObject(item)) {
|
|
60
|
-
item['~ignoreBuildChecks'] = value._ref.ignoreBuildChecks;
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return result;
|
|
66
|
-
}
|
|
67
|
-
if (value._var) {
|
|
68
|
-
if (type.isString(value._var)) {
|
|
69
|
-
const varValue = get(this.vars, value._var, {
|
|
70
|
-
default: null
|
|
71
|
-
});
|
|
72
|
-
return copyVarValue(varValue, this.sourceRefId);
|
|
73
|
-
}
|
|
74
|
-
if (type.isObject(value._var) && type.isString(value._var.key)) {
|
|
75
|
-
const varKey = value._var.key;
|
|
76
|
-
const varFromParent = get(this.vars, varKey);
|
|
77
|
-
// Check if var was explicitly provided (even if null) vs not provided at all (undefined)
|
|
78
|
-
// - Var provided (including null): use parent's sourceRefId for location
|
|
79
|
-
// - Var not provided (undefined): use default, preserve template's location
|
|
80
|
-
if (!type.isUndefined(varFromParent)) {
|
|
81
|
-
// Var was explicitly provided from parent file - use parent's location
|
|
82
|
-
return copyVarValue(varFromParent, this.sourceRefId);
|
|
83
|
-
}
|
|
84
|
-
// Using default value defined in template - preserve template's location markers
|
|
85
|
-
// Pass null for sourceRefId so we don't override the template file's ~r
|
|
86
|
-
const defaultValue = type.isNone(value._var.default) ? null : value._var.default;
|
|
87
|
-
return copyVarValue(defaultValue, null);
|
|
88
|
-
}
|
|
89
|
-
throw new Error('_var operator takes a string or object with "key" field as arguments.');
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return value;
|
|
93
|
-
}
|
|
94
|
-
function populateRefs({ parsedFiles, refDef, toPopulate }) {
|
|
95
|
-
// Use serializer.copy to preserve non-enumerable properties like ~r, ~k, ~l
|
|
96
|
-
// sourceRefId is the PARENT file's ref ID where vars are defined (not the template's ID)
|
|
97
|
-
return serializer.copy(toPopulate, {
|
|
98
|
-
reviver: refReviver.bind({
|
|
99
|
-
parsedFiles,
|
|
100
|
-
vars: refDef.vars,
|
|
101
|
-
sourceRefId: refDef.parent
|
|
102
|
-
})
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
export default populateRefs;
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ import { serializer, type } from '@lowdefy/helpers';
|
|
16
|
-
import { ConfigError } from '@lowdefy/errors';
|
|
17
|
-
import createRefReviver from './createRefReviver.js';
|
|
18
|
-
import evaluateBuildOperators from './evaluateBuildOperators.js';
|
|
19
|
-
import getKey from './getKey.js';
|
|
20
|
-
import getRefContent from './getRefContent.js';
|
|
21
|
-
import getRefPositions from '../jit/getRefPositions.js';
|
|
22
|
-
import getRefsFromFile from './getRefsFromFile.js';
|
|
23
|
-
import populateRefs from './populateRefs.js';
|
|
24
|
-
import runTransformer from './runTransformer.js';
|
|
25
|
-
import isPageContentPath from '../jit/isPageContentPath.js';
|
|
26
|
-
async function recursiveBuild({ context, refDef, count, content, referencedFrom, refChainSet = new Set(), refChainList = [], shallowOptions, jsonPath = '' }) {
|
|
27
|
-
// Detect circular references by tracking the chain of files being resolved
|
|
28
|
-
// Skip circular reference checking for refs without paths (e.g., resolver refs)
|
|
29
|
-
const currentPath = refDef.path;
|
|
30
|
-
if (currentPath) {
|
|
31
|
-
if (refChainSet.has(currentPath)) {
|
|
32
|
-
const chainDisplay = [
|
|
33
|
-
...refChainList,
|
|
34
|
-
currentPath
|
|
35
|
-
].join('\n -> ');
|
|
36
|
-
throw new ConfigError(`Circular reference detected. File "${currentPath}" references itself through:\n -> ${chainDisplay}`, {
|
|
37
|
-
filePath: referencedFrom ?? null,
|
|
38
|
-
lineNumber: referencedFrom ? refDef.lineNumber : null
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
refChainSet.add(currentPath);
|
|
42
|
-
refChainList.push(currentPath);
|
|
43
|
-
}
|
|
44
|
-
// Keep count as a fallback safety limit
|
|
45
|
-
if (count > 10000) {
|
|
46
|
-
throw new ConfigError('Maximum recursion depth of references exceeded (10000 levels). This likely indicates a circular reference.');
|
|
47
|
-
}
|
|
48
|
-
let fileContent = content ?? await getRefContent({
|
|
49
|
-
context,
|
|
50
|
-
refDef,
|
|
51
|
-
referencedFrom
|
|
52
|
-
});
|
|
53
|
-
const { foundRefs, fileContentBuiltRefs } = getRefsFromFile(fileContent, refDef.id, context.refMap);
|
|
54
|
-
const parsedFiles = {};
|
|
55
|
-
// Compute the JSON path position of each ref within the current file's content.
|
|
56
|
-
// This lets us check if a ref should be skipped during shallow builds.
|
|
57
|
-
const refPositions = shallowOptions ? getRefPositions(fileContentBuiltRefs, jsonPath) : null;
|
|
58
|
-
// Since we can have references in the variables of a reference, we need to first parse
|
|
59
|
-
// the deeper nodes, so we can use those parsed files in references higher in the tree.
|
|
60
|
-
// To do this, since foundRefs is an array of ref definitions that are in order of the
|
|
61
|
-
// deepest nodes first we for loop over over foundRefs one by one, awaiting each result.
|
|
62
|
-
for (const newRefDef of foundRefs.values()){
|
|
63
|
-
// Check if this ref should be skipped (shallow build)
|
|
64
|
-
const refJsonPath = refPositions?.get(newRefDef.id) ?? '';
|
|
65
|
-
if (shallowOptions && isPageContentPath(refJsonPath)) {
|
|
66
|
-
// Store shallow marker instead of resolving
|
|
67
|
-
parsedFiles[newRefDef.id] = {
|
|
68
|
-
'~shallow': true,
|
|
69
|
-
_ref: newRefDef.original,
|
|
70
|
-
_refId: newRefDef.id
|
|
71
|
-
};
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
// Parse vars and path before passing down to parse new file
|
|
75
|
-
const parsedRefDef = populateRefs({
|
|
76
|
-
toPopulate: newRefDef,
|
|
77
|
-
parsedFiles,
|
|
78
|
-
refDef
|
|
79
|
-
});
|
|
80
|
-
context.refMap[parsedRefDef.id].path = parsedRefDef.path;
|
|
81
|
-
// Store original definition for resolver refs so JIT can re-run them
|
|
82
|
-
if (!parsedRefDef.path) {
|
|
83
|
-
context.refMap[parsedRefDef.id].original = newRefDef.original;
|
|
84
|
-
}
|
|
85
|
-
if (Object.keys(newRefDef.vars).length > 0) {
|
|
86
|
-
context.unresolvedRefVars[newRefDef.id] = newRefDef.vars;
|
|
87
|
-
}
|
|
88
|
-
const parsedFile = await recursiveBuild({
|
|
89
|
-
context,
|
|
90
|
-
refDef: parsedRefDef,
|
|
91
|
-
count: count + 1,
|
|
92
|
-
referencedFrom: refDef.path ?? referencedFrom,
|
|
93
|
-
refChainSet,
|
|
94
|
-
refChainList,
|
|
95
|
-
shallowOptions,
|
|
96
|
-
jsonPath: refJsonPath
|
|
97
|
-
});
|
|
98
|
-
const transformedFile = await runTransformer({
|
|
99
|
-
context,
|
|
100
|
-
input: parsedFile,
|
|
101
|
-
refDef: parsedRefDef
|
|
102
|
-
});
|
|
103
|
-
// Evaluated in recursive loop for better error messages
|
|
104
|
-
const evaluatedOperators = await evaluateBuildOperators({
|
|
105
|
-
context,
|
|
106
|
-
input: transformedFile,
|
|
107
|
-
refDef: parsedRefDef
|
|
108
|
-
});
|
|
109
|
-
const withRefKey = getKey({
|
|
110
|
-
input: evaluatedOperators,
|
|
111
|
-
refDef: parsedRefDef
|
|
112
|
-
});
|
|
113
|
-
// Use serializer.copy to preserve non-enumerable properties like ~l.
|
|
114
|
-
// Only set ~r if not already present to preserve original file references from nested imports.
|
|
115
|
-
// Use child file's ref ID (parsedRefDef.id) not parent's (refDef.id) for correct error tracing.
|
|
116
|
-
const reviver = createRefReviver(parsedRefDef.id);
|
|
117
|
-
parsedFiles[newRefDef.id] = serializer.copy(withRefKey, {
|
|
118
|
-
reviver
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
// Backtrack: remove current file from chain so sibling refs can use it
|
|
122
|
-
// Only remove if it was added (i.e., if currentPath exists)
|
|
123
|
-
if (currentPath) {
|
|
124
|
-
refChainSet.delete(currentPath);
|
|
125
|
-
refChainList.pop();
|
|
126
|
-
}
|
|
127
|
-
return populateRefs({
|
|
128
|
-
toPopulate: fileContentBuiltRefs,
|
|
129
|
-
parsedFiles,
|
|
130
|
-
refDef
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
export default recursiveBuild;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ import { type } from '@lowdefy/helpers';
|
|
16
|
-
function getRefPositions(content, basePath) {
|
|
17
|
-
const positions = new Map();
|
|
18
|
-
walk(content, basePath, positions);
|
|
19
|
-
return positions;
|
|
20
|
-
}
|
|
21
|
-
function walk(node, path, positions) {
|
|
22
|
-
if (type.isObject(node)) {
|
|
23
|
-
if (node._ref && node._ref.id !== undefined) {
|
|
24
|
-
positions.set(node._ref.id, path);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
for (const key of Object.keys(node)){
|
|
28
|
-
const childPath = path ? `${path}.${key}` : key;
|
|
29
|
-
walk(node[key], childPath, positions);
|
|
30
|
-
}
|
|
31
|
-
} else if (type.isArray(node)) {
|
|
32
|
-
for(let i = 0; i < node.length; i++){
|
|
33
|
-
const childPath = path ? `${path}.${i}` : `${i}`;
|
|
34
|
-
walk(node[i], childPath, positions);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
export default getRefPositions;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ import PAGE_CONTENT_KEYS from './pageContentKeys.js';
|
|
16
|
-
function stripPageContent({ components }) {
|
|
17
|
-
for (const page of components.pages ?? []){
|
|
18
|
-
for (const key of PAGE_CONTENT_KEYS){
|
|
19
|
-
delete page[key];
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
export default stripPageContent;
|