@lowdefy/build 4.7.2 → 5.0.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/addDefaultPages/404.js +8 -2
- package/dist/build/buildApi/buildRoutine/validateStep.js +7 -5
- package/dist/build/buildApi/validateEndpoint.js +6 -5
- package/dist/build/buildConnections.js +6 -0
- package/dist/build/buildImports/buildIconImports.js +5 -1
- package/dist/build/buildImports/buildImportsDev.js +1 -6
- package/dist/build/buildImports/buildImportsProd.js +1 -6
- package/dist/build/buildImports/validateIconImports.js +65 -0
- package/dist/build/buildJs/jsMapParser.js +5 -2
- package/dist/build/buildPages/buildBlock/buildBlock.js +12 -4
- package/dist/build/buildPages/buildBlock/buildEvents.js +34 -1
- package/dist/build/buildPages/buildBlock/buildRequests.js +7 -5
- package/dist/build/buildPages/buildBlock/buildSubBlocks.js +9 -9
- package/dist/build/buildPages/buildBlock/countBlockOperators.js +1 -1
- package/dist/build/buildPages/buildBlock/moveAreasToSlots.js +31 -0
- package/dist/build/buildPages/buildBlock/{moveSkeletonBlocksToArea.js → moveSkeletonBlocksToSlot.js} +8 -8
- package/dist/build/buildPages/buildBlock/{moveSubBlocksToArea.js → moveSubBlocksToSlot.js} +3 -3
- package/dist/build/buildPages/buildBlock/normalizeClassAndStyles.js +124 -0
- package/dist/build/buildPages/buildBlock/normalizeLayout.js +68 -0
- package/dist/build/buildPages/buildBlock/setBlockId.js +7 -1
- package/dist/build/buildPages/buildBlock/validateSlots.js +34 -0
- package/dist/build/buildPages/buildPage.js +23 -1
- package/dist/build/buildRefs/addLineNumbers.js +76 -0
- package/dist/build/{buildImports/buildStyleImports.js → buildRefs/getLineNumber.js} +4 -10
- package/dist/build/buildRefs/getRefContent.js +9 -1
- package/dist/build/buildRefs/parseRefContent.js +4 -66
- package/dist/build/buildTypes.js +4 -2
- package/dist/build/cleanBuildDirectory.js +3 -1
- package/dist/build/collectPageContent.js +57 -0
- package/dist/build/jit/buildPageJit.js +14 -3
- package/dist/build/jit/collectSkeletonSourceFiles.js +75 -0
- package/dist/build/jit/extractIconData.js +16 -1
- package/dist/build/jit/pageContentKeys.js +1 -0
- package/dist/build/jit/shallowBuild.js +34 -1
- package/dist/build/jit/stripPageContent.js +29 -0
- package/dist/build/jit/writePageJit.js +9 -1
- package/dist/build/testSchema.js +3 -0
- package/dist/build/writePluginImports/collectBlockSourceContent.js +65 -0
- package/dist/build/writePluginImports/writeActionSchemaMap.js +1 -1
- package/dist/build/writePluginImports/writeBlockSchemaMap.js +45 -7
- package/dist/build/writePluginImports/writeGlobalsCss.js +126 -0
- package/dist/build/writePluginImports/writeOperatorSchemaMap.js +1 -1
- package/dist/build/writePluginImports/writePluginImports.js +7 -2
- package/dist/build/writeTheme.js +28 -0
- package/dist/createContext.js +2 -0
- package/dist/defaultTypesMap.js +1693 -837
- package/dist/index.js +16 -0
- package/dist/lowdefySchema.js +100 -0
- package/dist/scripts/generateDefaultTypes.js +5 -10
- package/dist/test-utils/runBuild.js +3 -0
- package/dist/test-utils/runBuildForSnapshots.js +5 -2
- package/dist/test-utils/testContext.js +2 -1
- package/dist/utils/createHandleWarning.js +3 -0
- package/dist/utils/createPluginTypesMap.js +5 -9
- package/dist/utils/validateId.js +24 -0
- package/package.json +47 -47
- package/dist/build/writePluginImports/writeStyleImports.js +0 -34
|
@@ -0,0 +1,68 @@
|
|
|
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 { ConfigWarning } from '@lowdefy/errors';
|
|
17
|
+
const DEPRECATED_LAYOUT_KEYS = {
|
|
18
|
+
contentGutter: 'gap',
|
|
19
|
+
contentGap: 'gap',
|
|
20
|
+
contentJustify: 'justify',
|
|
21
|
+
contentDirection: 'direction',
|
|
22
|
+
contentWrap: 'wrap',
|
|
23
|
+
contentOverflow: 'overflow'
|
|
24
|
+
};
|
|
25
|
+
function normalizeLayout(block, pageContext) {
|
|
26
|
+
const layout = block.layout;
|
|
27
|
+
if (type.isNone(layout) || !type.isObject(layout)) return;
|
|
28
|
+
// Warn and rename deprecated content* layout properties
|
|
29
|
+
for (const [oldKey, newKey] of Object.entries(DEPRECATED_LAYOUT_KEYS)){
|
|
30
|
+
if (!type.isNone(layout[oldKey])) {
|
|
31
|
+
pageContext.context.handleWarning(new ConfigWarning(`Block "${block.blockId}": layout.${oldKey} is deprecated. Use layout.${newKey} instead.`, {
|
|
32
|
+
configKey: block['~k'],
|
|
33
|
+
prodError: true
|
|
34
|
+
}));
|
|
35
|
+
if (type.isNone(layout[newKey])) {
|
|
36
|
+
layout[newKey] = layout[oldKey];
|
|
37
|
+
}
|
|
38
|
+
delete layout[oldKey];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Warn about gutter in slot/area configs
|
|
42
|
+
for (const slot of Object.values(block.slots ?? {})){
|
|
43
|
+
if (!type.isNone(slot.gutter)) {
|
|
44
|
+
pageContext.context.handleWarning(new ConfigWarning(`Block "${block.blockId}": slots.*.gutter is deprecated. Use gap instead.`, {
|
|
45
|
+
configKey: block['~k'],
|
|
46
|
+
prodError: true
|
|
47
|
+
}));
|
|
48
|
+
if (type.isNone(slot.gap)) {
|
|
49
|
+
slot.gap = slot.gutter;
|
|
50
|
+
}
|
|
51
|
+
delete slot.gutter;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Also check areas (before they're moved to slots)
|
|
55
|
+
for (const area of Object.values(block.areas ?? {})){
|
|
56
|
+
if (!type.isNone(area.gutter)) {
|
|
57
|
+
pageContext.context.handleWarning(new ConfigWarning(`Block "${block.blockId}": areas.*.gutter is deprecated. Use gap instead.`, {
|
|
58
|
+
configKey: block['~k'],
|
|
59
|
+
prodError: true
|
|
60
|
+
}));
|
|
61
|
+
if (type.isNone(area.gap)) {
|
|
62
|
+
area.gap = area.gutter;
|
|
63
|
+
}
|
|
64
|
+
delete area.gutter;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export default normalizeLayout;
|
|
@@ -12,8 +12,14 @@
|
|
|
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
|
-
*/
|
|
15
|
+
*/ import { ConfigError } from '@lowdefy/errors';
|
|
16
|
+
function setBlockId(block, { pageId, blockIdCounter }) {
|
|
16
17
|
block.blockId = block.id;
|
|
18
|
+
if (block.blockId === pageId && blockIdCounter.getCount(block.blockId) > 0) {
|
|
19
|
+
throw new ConfigError(`Block id "${block.blockId}" on page "${pageId}" collides with the page id. A block cannot have the same id as its page.`, {
|
|
20
|
+
configKey: block['~k']
|
|
21
|
+
});
|
|
22
|
+
}
|
|
17
23
|
block.id = `block:${pageId}:${block.blockId}:${blockIdCounter.getCount(block.blockId)}`;
|
|
18
24
|
blockIdCounter.increment(block.blockId);
|
|
19
25
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
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 { ConfigWarning } from '@lowdefy/errors';
|
|
17
|
+
function validateSlots(block, pageContext) {
|
|
18
|
+
const blockMeta = pageContext.context.blockMetas?.[block.type];
|
|
19
|
+
if (!blockMeta) return;
|
|
20
|
+
if (blockMeta.slots === false) return;
|
|
21
|
+
if (!type.isArray(blockMeta.slots) && !type.isObject(blockMeta.slots)) return;
|
|
22
|
+
if (!type.isObject(block.slots)) return;
|
|
23
|
+
const validSlots = type.isArray(blockMeta.slots) ? new Set(blockMeta.slots) : new Set(Object.keys(blockMeta.slots));
|
|
24
|
+
for (const slotKey of Object.keys(block.slots)){
|
|
25
|
+
if (!validSlots.has(slotKey)) {
|
|
26
|
+
pageContext.context.handleWarning(new ConfigWarning(`Block "${block.blockId}" (${block.type}): Unknown slot "${slotKey}". Valid slots: ${[
|
|
27
|
+
...validSlots
|
|
28
|
+
].join(', ')}.`, {
|
|
29
|
+
configKey: block.slots[slotKey]?.['~k'] ?? block['~k']
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export default validateSlots;
|
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import { type } from '@lowdefy/helpers';
|
|
16
|
-
import { ConfigError } from '@lowdefy/errors';
|
|
16
|
+
import { ConfigError, ConfigWarning } from '@lowdefy/errors';
|
|
17
17
|
import buildBlock from './buildBlock/buildBlock.js';
|
|
18
18
|
import collectExceptions from '../../utils/collectExceptions.js';
|
|
19
19
|
import createCheckDuplicateId from '../../utils/createCheckDuplicateId.js';
|
|
20
|
+
import validateId from '../../utils/validateId.js';
|
|
20
21
|
import createCounter from '../../utils/createCounter.js';
|
|
21
22
|
import validateRequestReferences from './validateRequestReferences.js';
|
|
22
23
|
function buildPage({ page, index, context, checkDuplicatePageId }) {
|
|
@@ -38,6 +39,11 @@ function buildPage({ page, index, context, checkDuplicatePageId }) {
|
|
|
38
39
|
failed: true
|
|
39
40
|
};
|
|
40
41
|
}
|
|
42
|
+
validateId({
|
|
43
|
+
id: page.id,
|
|
44
|
+
field: 'Page id',
|
|
45
|
+
configKey
|
|
46
|
+
});
|
|
41
47
|
if (checkDuplicatePageId) {
|
|
42
48
|
checkDuplicatePageId({
|
|
43
49
|
id: page.id,
|
|
@@ -47,6 +53,7 @@ function buildPage({ page, index, context, checkDuplicatePageId }) {
|
|
|
47
53
|
page.pageId = page.id;
|
|
48
54
|
const requests = [];
|
|
49
55
|
const requestActionRefs = [];
|
|
56
|
+
const shortcutRefs = [];
|
|
50
57
|
buildBlock(page, {
|
|
51
58
|
auth: page.auth,
|
|
52
59
|
blockIdCounter: createCounter(),
|
|
@@ -57,6 +64,7 @@ function buildPage({ page, index, context, checkDuplicatePageId }) {
|
|
|
57
64
|
pageId: page.pageId,
|
|
58
65
|
requests,
|
|
59
66
|
requestActionRefs,
|
|
67
|
+
shortcutRefs,
|
|
60
68
|
linkActionRefs: context.linkActionRefs,
|
|
61
69
|
typeCounters: context.typeCounters
|
|
62
70
|
});
|
|
@@ -69,6 +77,20 @@ function buildPage({ page, index, context, checkDuplicatePageId }) {
|
|
|
69
77
|
pageId: page.pageId,
|
|
70
78
|
context
|
|
71
79
|
});
|
|
80
|
+
// Warn on duplicate shortcuts within the page
|
|
81
|
+
const seenShortcuts = {};
|
|
82
|
+
shortcutRefs.forEach(({ shortcut, blockId, eventId, configKey })=>{
|
|
83
|
+
if (seenShortcuts[shortcut]) {
|
|
84
|
+
context.handleWarning(new ConfigWarning(`Duplicate shortcut "${shortcut}" on event "${eventId}" on block "${blockId}" on page "${page.pageId}" — already defined on block "${seenShortcuts[shortcut].blockId}".`, {
|
|
85
|
+
configKey
|
|
86
|
+
}));
|
|
87
|
+
} else {
|
|
88
|
+
seenShortcuts[shortcut] = {
|
|
89
|
+
blockId,
|
|
90
|
+
eventId
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
});
|
|
72
94
|
page.requests = requests;
|
|
73
95
|
}
|
|
74
96
|
export default buildPage;
|
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
*/ /* eslint-disable no-param-reassign */ import { isMap, isSeq, isPair, isScalar } from 'yaml';
|
|
16
|
+
import setNonEnumerableProperty from '../../utils/setNonEnumerableProperty.js';
|
|
17
|
+
import getLineNumber from './getLineNumber.js';
|
|
18
|
+
function addLineNumbers(node, content, result) {
|
|
19
|
+
if (isMap(node)) {
|
|
20
|
+
const obj = result || {};
|
|
21
|
+
if (node.range) {
|
|
22
|
+
setNonEnumerableProperty(obj, '~l', getLineNumber(content, node.range[0]));
|
|
23
|
+
}
|
|
24
|
+
for (const pair of node.items){
|
|
25
|
+
if (isPair(pair) && isScalar(pair.key)) {
|
|
26
|
+
const key = pair.key.value;
|
|
27
|
+
const value = pair.value;
|
|
28
|
+
// Use key's line number for the value's ~l (more useful for error messages)
|
|
29
|
+
const keyLineNumber = pair.key.range ? getLineNumber(content, pair.key.range[0]) : null;
|
|
30
|
+
if (isMap(value)) {
|
|
31
|
+
const mapResult = addLineNumbers(value, content, {});
|
|
32
|
+
// Override ~l with key's line number if available
|
|
33
|
+
if (keyLineNumber) {
|
|
34
|
+
setNonEnumerableProperty(mapResult, '~l', keyLineNumber);
|
|
35
|
+
}
|
|
36
|
+
obj[key] = mapResult;
|
|
37
|
+
} else if (isSeq(value)) {
|
|
38
|
+
const arrResult = addLineNumbers(value, content, []);
|
|
39
|
+
// Override ~l with key's line number if available
|
|
40
|
+
if (keyLineNumber) {
|
|
41
|
+
setNonEnumerableProperty(arrResult, '~l', keyLineNumber);
|
|
42
|
+
}
|
|
43
|
+
obj[key] = arrResult;
|
|
44
|
+
} else if (isScalar(value)) {
|
|
45
|
+
obj[key] = value.value;
|
|
46
|
+
} else {
|
|
47
|
+
obj[key] = value?.toJSON?.() ?? value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return obj;
|
|
52
|
+
}
|
|
53
|
+
if (isSeq(node)) {
|
|
54
|
+
const arr = result || [];
|
|
55
|
+
if (node.range) {
|
|
56
|
+
setNonEnumerableProperty(arr, '~l', getLineNumber(content, node.range[0]));
|
|
57
|
+
}
|
|
58
|
+
for (const item of node.items){
|
|
59
|
+
if (isMap(item)) {
|
|
60
|
+
arr.push(addLineNumbers(item, content, {}));
|
|
61
|
+
} else if (isSeq(item)) {
|
|
62
|
+
arr.push(addLineNumbers(item, content, []));
|
|
63
|
+
} else if (isScalar(item)) {
|
|
64
|
+
arr.push(item.value);
|
|
65
|
+
} else {
|
|
66
|
+
arr.push(item?.toJSON?.() ?? item);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return arr;
|
|
70
|
+
}
|
|
71
|
+
if (isScalar(node)) {
|
|
72
|
+
return node.value;
|
|
73
|
+
}
|
|
74
|
+
return node?.toJSON?.() ?? node;
|
|
75
|
+
}
|
|
76
|
+
export default addLineNumbers;
|
|
@@ -12,14 +12,8 @@
|
|
|
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
|
-
*/ function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
styles.add(...(context.typesMap.styles.packages[block.package] || []).map((style)=>`${block.package}/${style}`));
|
|
19
|
-
styles.add(...(context.typesMap.styles.blocks[block.typeName] || []).map((style)=>`${block.package}/${style}`));
|
|
20
|
-
});
|
|
21
|
-
return [
|
|
22
|
-
...styles
|
|
23
|
-
].filter((style)=>!!style);
|
|
15
|
+
*/ function getLineNumber(content, offset) {
|
|
16
|
+
if (offset == null || offset < 0) return null;
|
|
17
|
+
return content.substring(0, offset).split('\n').length;
|
|
24
18
|
}
|
|
25
|
-
export default
|
|
19
|
+
export default getLineNumber;
|
|
@@ -12,7 +12,10 @@
|
|
|
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
|
|
15
|
+
*/ import { type } from '@lowdefy/helpers';
|
|
16
|
+
import { getFileExtension } from '@lowdefy/node-utils';
|
|
17
|
+
import getConfigFile from './getConfigFile.js';
|
|
18
|
+
import getUserJavascriptFunction from './getUserJavascriptFunction.js';
|
|
16
19
|
import parseRefContent from './parseRefContent.js';
|
|
17
20
|
import runRefResolver from './runRefResolver.js';
|
|
18
21
|
async function getRefContent({ context, refDef, referencedFrom }) {
|
|
@@ -29,6 +32,11 @@ async function getRefContent({ context, refDef, referencedFrom }) {
|
|
|
29
32
|
refDef,
|
|
30
33
|
referencedFrom
|
|
31
34
|
});
|
|
35
|
+
} else if (type.isString(refDef.path) && getFileExtension(refDef.path) === 'js') {
|
|
36
|
+
return getUserJavascriptFunction({
|
|
37
|
+
context,
|
|
38
|
+
filePath: refDef.path
|
|
39
|
+
});
|
|
32
40
|
} else {
|
|
33
41
|
content = await getConfigFile({
|
|
34
42
|
context,
|
|
@@ -12,75 +12,13 @@
|
|
|
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
|
-
*/
|
|
15
|
+
*/ import { ConfigError } from '@lowdefy/errors';
|
|
16
16
|
import { type } from '@lowdefy/helpers';
|
|
17
17
|
import { getFileExtension, getFileSubExtension } from '@lowdefy/node-utils';
|
|
18
18
|
import JSON5 from 'json5';
|
|
19
|
-
import YAML
|
|
19
|
+
import YAML from 'yaml';
|
|
20
|
+
import addLineNumbers from './addLineNumbers.js';
|
|
20
21
|
import parseNunjucks from './parseNunjucks.js';
|
|
21
|
-
import setNonEnumerableProperty from '../../utils/setNonEnumerableProperty.js';
|
|
22
|
-
function getLineNumber(content, offset) {
|
|
23
|
-
if (offset == null || offset < 0) return null;
|
|
24
|
-
return content.substring(0, offset).split('\n').length;
|
|
25
|
-
}
|
|
26
|
-
function addLineNumbers(node, content, result) {
|
|
27
|
-
if (isMap(node)) {
|
|
28
|
-
const obj = result || {};
|
|
29
|
-
if (node.range) {
|
|
30
|
-
setNonEnumerableProperty(obj, '~l', getLineNumber(content, node.range[0]));
|
|
31
|
-
}
|
|
32
|
-
for (const pair of node.items){
|
|
33
|
-
if (isPair(pair) && isScalar(pair.key)) {
|
|
34
|
-
const key = pair.key.value;
|
|
35
|
-
const value = pair.value;
|
|
36
|
-
// Use key's line number for the value's ~l (more useful for error messages)
|
|
37
|
-
const keyLineNumber = pair.key.range ? getLineNumber(content, pair.key.range[0]) : null;
|
|
38
|
-
if (isMap(value)) {
|
|
39
|
-
const mapResult = addLineNumbers(value, content, {});
|
|
40
|
-
// Override ~l with key's line number if available
|
|
41
|
-
if (keyLineNumber) {
|
|
42
|
-
setNonEnumerableProperty(mapResult, '~l', keyLineNumber);
|
|
43
|
-
}
|
|
44
|
-
obj[key] = mapResult;
|
|
45
|
-
} else if (isSeq(value)) {
|
|
46
|
-
const arrResult = addLineNumbers(value, content, []);
|
|
47
|
-
// Override ~l with key's line number if available
|
|
48
|
-
if (keyLineNumber) {
|
|
49
|
-
setNonEnumerableProperty(arrResult, '~l', keyLineNumber);
|
|
50
|
-
}
|
|
51
|
-
obj[key] = arrResult;
|
|
52
|
-
} else if (isScalar(value)) {
|
|
53
|
-
obj[key] = value.value;
|
|
54
|
-
} else {
|
|
55
|
-
obj[key] = value?.toJSON?.() ?? value;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return obj;
|
|
60
|
-
}
|
|
61
|
-
if (isSeq(node)) {
|
|
62
|
-
const arr = result || [];
|
|
63
|
-
if (node.range) {
|
|
64
|
-
setNonEnumerableProperty(arr, '~l', getLineNumber(content, node.range[0]));
|
|
65
|
-
}
|
|
66
|
-
for (const item of node.items){
|
|
67
|
-
if (isMap(item)) {
|
|
68
|
-
arr.push(addLineNumbers(item, content, {}));
|
|
69
|
-
} else if (isSeq(item)) {
|
|
70
|
-
arr.push(addLineNumbers(item, content, []));
|
|
71
|
-
} else if (isScalar(item)) {
|
|
72
|
-
arr.push(item.value);
|
|
73
|
-
} else {
|
|
74
|
-
arr.push(item?.toJSON?.() ?? item);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return arr;
|
|
78
|
-
}
|
|
79
|
-
if (isScalar(node)) {
|
|
80
|
-
return node.value;
|
|
81
|
-
}
|
|
82
|
-
return node?.toJSON?.() ?? node;
|
|
83
|
-
}
|
|
84
22
|
function parseYamlWithLineNumbers(content) {
|
|
85
23
|
const doc = YAML.parseDocument(content);
|
|
86
24
|
if (doc.errors && doc.errors.length > 0) {
|
|
@@ -88,7 +26,7 @@ function parseYamlWithLineNumbers(content) {
|
|
|
88
26
|
}
|
|
89
27
|
return addLineNumbers(doc.contents, content);
|
|
90
28
|
}
|
|
91
|
-
function parseRefContent({ content, refDef }) {
|
|
29
|
+
async function parseRefContent({ content, refDef }) {
|
|
92
30
|
const { path, vars } = refDef;
|
|
93
31
|
if (type.isString(path)) {
|
|
94
32
|
let ext = getFileExtension(path);
|
package/dist/build/buildTypes.js
CHANGED
|
@@ -12,9 +12,9 @@
|
|
|
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
|
|
15
|
+
*/ import { ConfigError, ConfigWarning } from '@lowdefy/errors';
|
|
16
|
+
import basicTypes from '@lowdefy/blocks-basic/types';
|
|
16
17
|
import loaderTypes from '@lowdefy/blocks-loaders/types';
|
|
17
|
-
import { ConfigError, ConfigWarning } from '@lowdefy/errors';
|
|
18
18
|
import findSimilarString from '../utils/findSimilarString.js';
|
|
19
19
|
function buildTypeClass(context, { counter, definitions, store, typeClass, warnIfMissing = false }) {
|
|
20
20
|
const counts = counter.getCounts();
|
|
@@ -61,6 +61,8 @@ function buildTypes({ components, context }) {
|
|
|
61
61
|
loaderTypes.blocks.forEach((block)=>typeCounters.blocks.increment(block));
|
|
62
62
|
// Used for DisplayMessage in @lowdefy/client
|
|
63
63
|
typeCounters.blocks.increment('Message');
|
|
64
|
+
// Used by blocks-antd Header/PageHeaderMenu/PageSiderMenu darkModeToggle
|
|
65
|
+
typeCounters.actions.increment('SetDarkMode');
|
|
64
66
|
components.types = {
|
|
65
67
|
actions: {},
|
|
66
68
|
auth: {
|
|
@@ -12,8 +12,10 @@
|
|
|
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
|
|
15
|
+
*/ import path from 'path';
|
|
16
|
+
import { cleanDirectory } from '@lowdefy/node-utils';
|
|
16
17
|
async function cleanBuildDirectory({ context }) {
|
|
17
18
|
await cleanDirectory(context.directories.build);
|
|
19
|
+
await cleanDirectory(path.join(context.directories.server, 'lowdefy-build', 'tailwind'));
|
|
18
20
|
}
|
|
19
21
|
export default cleanBuildDirectory;
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
*/ function walkValue(value, strings) {
|
|
16
|
+
if (typeof value === 'string') {
|
|
17
|
+
strings.push(value);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (Array.isArray(value)) {
|
|
21
|
+
for (const item of value){
|
|
22
|
+
walkValue(item, strings);
|
|
23
|
+
}
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (value && typeof value === 'object') {
|
|
27
|
+
for (const v of Object.values(value)){
|
|
28
|
+
walkValue(v, strings);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function walkBlockProperties(blocks, strings) {
|
|
33
|
+
if (!Array.isArray(blocks)) return;
|
|
34
|
+
for (const block of blocks){
|
|
35
|
+
if (!block) continue;
|
|
36
|
+
if (block.class) {
|
|
37
|
+
walkValue(block.class, strings);
|
|
38
|
+
}
|
|
39
|
+
if (block.properties) {
|
|
40
|
+
walkValue(block.properties, strings);
|
|
41
|
+
}
|
|
42
|
+
walkBlockProperties(block.blocks, strings);
|
|
43
|
+
for (const area of Object.values(block.areas ?? {})){
|
|
44
|
+
walkBlockProperties(area.blocks, strings);
|
|
45
|
+
}
|
|
46
|
+
for (const slot of Object.values(block.slots ?? {})){
|
|
47
|
+
walkBlockProperties(slot.blocks, strings);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function collectPageContent(pages) {
|
|
52
|
+
const strings = [];
|
|
53
|
+
walkBlockProperties(pages, strings);
|
|
54
|
+
return strings.join('\n');
|
|
55
|
+
}
|
|
56
|
+
export default collectPageContent;
|
|
57
|
+
export { walkBlockProperties };
|
|
@@ -70,11 +70,13 @@ async function buildPageJit({ pageId, pageRegistry, context, directories, logger
|
|
|
70
70
|
if (!pageEntry) {
|
|
71
71
|
return null;
|
|
72
72
|
}
|
|
73
|
-
// Reset errors for this build. Keep
|
|
74
|
-
// JIT builds (different pages sharing buildContext) cannot corrupt
|
|
75
|
-
//
|
|
73
|
+
// Reset errors and warnings for this build. Keep local references so that
|
|
74
|
+
// concurrent JIT builds (different pages sharing buildContext) cannot corrupt
|
|
75
|
+
// our lists by reassigning during an await.
|
|
76
76
|
const buildErrors = [];
|
|
77
|
+
const buildWarnings = [];
|
|
77
78
|
buildContext.errors = buildErrors;
|
|
79
|
+
buildContext.warnings = buildWarnings;
|
|
78
80
|
try {
|
|
79
81
|
// Pages without a source file (e.g., default 404) can only be served from
|
|
80
82
|
// their pre-built artifact — they have no YAML to re-resolve from.
|
|
@@ -277,6 +279,15 @@ async function buildPageJit({ pageId, pageRegistry, context, directories, logger
|
|
|
277
279
|
page: finalPage,
|
|
278
280
|
context: buildContext
|
|
279
281
|
});
|
|
282
|
+
// Attach warnings after disk write so they don't persist in artifacts
|
|
283
|
+
if (buildWarnings.length > 0) {
|
|
284
|
+
finalPage._warnings = buildWarnings.map((w)=>({
|
|
285
|
+
type: w.name ?? 'ConfigWarning',
|
|
286
|
+
message: w.message,
|
|
287
|
+
source: w.source ?? null,
|
|
288
|
+
stack: w.stack ?? null
|
|
289
|
+
}));
|
|
290
|
+
}
|
|
280
291
|
return finalPage;
|
|
281
292
|
} catch (err) {
|
|
282
293
|
// Attach any collected errors to the thrown error
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
*/ // Walk an object/array tree collecting non-enumerable ~r ref IDs.
|
|
16
|
+
// ~r is set by buildRefs (walker.js) on every resolved object/array
|
|
17
|
+
// and is non-enumerable, so Object.keys() won't find it.
|
|
18
|
+
function walkRefIds(obj, refIds) {
|
|
19
|
+
if (obj === null || typeof obj !== 'object') return;
|
|
20
|
+
if (obj['~r'] !== undefined) {
|
|
21
|
+
refIds.add(obj['~r']);
|
|
22
|
+
}
|
|
23
|
+
if (Array.isArray(obj)) {
|
|
24
|
+
for(let i = 0; i < obj.length; i++){
|
|
25
|
+
walkRefIds(obj[i], refIds);
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
for (const key of Object.keys(obj)){
|
|
29
|
+
walkRefIds(obj[key], refIds);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Collect file paths that contribute to skeleton (non-page) config.
|
|
34
|
+
// Walks ~r markers on non-page components, traces each through the
|
|
35
|
+
// refMap parent chain, and scans for scalar-resolving descendants.
|
|
36
|
+
function collectSkeletonSourceFiles({ components, context }) {
|
|
37
|
+
const refIds = new Set();
|
|
38
|
+
for (const key of Object.keys(components)){
|
|
39
|
+
if (key === 'pages') continue;
|
|
40
|
+
walkRefIds(components[key], refIds);
|
|
41
|
+
}
|
|
42
|
+
const sourceFiles = new Set();
|
|
43
|
+
// Walk parent chains for each collected ref ID
|
|
44
|
+
for (const refId of refIds){
|
|
45
|
+
let current = refId;
|
|
46
|
+
while(current != null){
|
|
47
|
+
const entry = context.refMap[current];
|
|
48
|
+
if (!entry) break;
|
|
49
|
+
if (entry.path) {
|
|
50
|
+
sourceFiles.add(entry.path);
|
|
51
|
+
}
|
|
52
|
+
current = entry.parent;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Scan for scalar-resolving descendants: refs that resolved to primitives
|
|
56
|
+
// have no ~r marker in the tree but are recorded in the refMap. Include
|
|
57
|
+
// their paths if any ancestor in their parent chain is a collected ref ID.
|
|
58
|
+
for (const [id, entry] of Object.entries(context.refMap)){
|
|
59
|
+
if (refIds.has(id)) continue;
|
|
60
|
+
let current = entry.parent;
|
|
61
|
+
while(current != null){
|
|
62
|
+
if (refIds.has(current)) {
|
|
63
|
+
if (entry.path) {
|
|
64
|
+
sourceFiles.add(entry.path);
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
const parentEntry = context.refMap[current];
|
|
69
|
+
if (!parentEntry) break;
|
|
70
|
+
current = parentEntry.parent;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return sourceFiles;
|
|
74
|
+
}
|
|
75
|
+
export default collectSkeletonSourceFiles;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import { createRequire } from 'module';
|
|
16
16
|
import path from 'path';
|
|
17
|
+
import findSimilarString from '../../utils/findSimilarString.js';
|
|
17
18
|
// Matches the JSON data argument inside GenIcon({...})(props) in react-icons source.
|
|
18
19
|
// react-icons icons are generated functions of the form:
|
|
19
20
|
// function IconName(props) { return GenIcon({...})(props); }
|
|
@@ -35,7 +36,21 @@ function extractIconData({ icons, directories, logger }) {
|
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
const iconFn = moduleCache[pkg][icon];
|
|
38
|
-
if (!iconFn)
|
|
39
|
+
if (!iconFn) {
|
|
40
|
+
if (logger) {
|
|
41
|
+
let message = `Icon "${icon}" not found in "${pkg}".`;
|
|
42
|
+
const suggestion = findSimilarString({
|
|
43
|
+
input: icon,
|
|
44
|
+
candidates: Object.keys(moduleCache[pkg]),
|
|
45
|
+
maxDistance: Math.max(3, Math.ceil(icon.length * 0.4))
|
|
46
|
+
});
|
|
47
|
+
if (suggestion) {
|
|
48
|
+
message += ` Did you mean "${suggestion}"?`;
|
|
49
|
+
}
|
|
50
|
+
logger.warn(message);
|
|
51
|
+
}
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
39
54
|
const match = iconFn.toString().match(genIconDataRegex);
|
|
40
55
|
if (match) {
|
|
41
56
|
try {
|