@lowdefy/build 4.7.3 → 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/extractIconData.js +16 -1
- package/dist/build/jit/pageContentKeys.js +1 -0
- package/dist/build/jit/shallowBuild.js +24 -0
- 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
|
|
@@ -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 {
|
|
@@ -40,6 +40,7 @@ import writeApi from '../writeApi.js';
|
|
|
40
40
|
import writeGlobal from '../writeGlobal.js';
|
|
41
41
|
import writeJs from '../buildJs/writeJs.js';
|
|
42
42
|
import writeLogger from '../writeLogger.js';
|
|
43
|
+
import writeTheme from '../writeTheme.js';
|
|
43
44
|
import writeMaps from '../writeMaps.js';
|
|
44
45
|
import updateServerPackageJson from '../full/updateServerPackageJson.js';
|
|
45
46
|
import writeMenus from '../writeMenus.js';
|
|
@@ -48,7 +49,9 @@ import writePluginImports from '../writePluginImports/writePluginImports.js';
|
|
|
48
49
|
import addInstalledTypes from './addInstalledTypes.js';
|
|
49
50
|
import buildJsShallow from './buildJsShallow.js';
|
|
50
51
|
import buildShallowPages from './buildShallowPages.js';
|
|
52
|
+
import collectPageContent from '../collectPageContent.js';
|
|
51
53
|
import collectSkeletonSourceFiles from './collectSkeletonSourceFiles.js';
|
|
54
|
+
import stripPageContent from './stripPageContent.js';
|
|
52
55
|
import writeSourcelessPages from './writeSourcelessPages.js';
|
|
53
56
|
async function shallowBuild(options) {
|
|
54
57
|
makeId.reset();
|
|
@@ -68,6 +71,10 @@ async function shallowBuild(options) {
|
|
|
68
71
|
}
|
|
69
72
|
throw err;
|
|
70
73
|
}
|
|
74
|
+
// Stop early if buildRefs collected errors (e.g., YAML parse errors).
|
|
75
|
+
// Failed _ref resolutions leave null entries in arrays — logging now
|
|
76
|
+
// surfaces the real error before downstream code crashes on nulls.
|
|
77
|
+
logCollectedErrors(context);
|
|
71
78
|
// Collect skeleton source files while ~r markers still exist on objects.
|
|
72
79
|
const skeletonSourceFiles = collectSkeletonSourceFiles({
|
|
73
80
|
components,
|
|
@@ -78,6 +85,19 @@ async function shallowBuild(options) {
|
|
|
78
85
|
components,
|
|
79
86
|
context
|
|
80
87
|
});
|
|
88
|
+
context.tailwindContentMap = new Map();
|
|
89
|
+
for (const page of components.pages ?? []){
|
|
90
|
+
const content = collectPageContent([
|
|
91
|
+
page
|
|
92
|
+
]);
|
|
93
|
+
if (content) {
|
|
94
|
+
context.tailwindContentMap.set(page.id, content);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
stripPageContent({
|
|
98
|
+
components,
|
|
99
|
+
context
|
|
100
|
+
});
|
|
81
101
|
tryBuildStep(testSchema, 'testSchema', {
|
|
82
102
|
components,
|
|
83
103
|
context
|
|
@@ -185,6 +205,10 @@ async function shallowBuild(options) {
|
|
|
185
205
|
components,
|
|
186
206
|
context
|
|
187
207
|
});
|
|
208
|
+
await writeTheme({
|
|
209
|
+
components,
|
|
210
|
+
context
|
|
211
|
+
});
|
|
188
212
|
await writeLogger({
|
|
189
213
|
components,
|
|
190
214
|
context
|
|
@@ -0,0 +1,29 @@
|
|
|
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 PAGE_CONTENT_KEYS from './pageContentKeys.js';
|
|
17
|
+
function stripPageContent({ components, context }) {
|
|
18
|
+
for (const page of components.pages ?? []){
|
|
19
|
+
// Only strip pages that have a source ref (will be JIT-rebuilt).
|
|
20
|
+
// Inline pages (no ~r) must keep their content for buildShallowPages.
|
|
21
|
+
const keyMapEntry = context.keyMap[page['~k']];
|
|
22
|
+
const refId = keyMapEntry?.['~r'] ?? null;
|
|
23
|
+
if (type.isNone(refId)) continue;
|
|
24
|
+
for (const key of PAGE_CONTENT_KEYS){
|
|
25
|
+
delete page[key];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export default stripPageContent;
|
|
@@ -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 path from 'path';
|
|
16
|
+
import { serializer, type } from '@lowdefy/helpers';
|
|
17
|
+
import { writeFile } from '@lowdefy/node-utils';
|
|
18
|
+
import collectPageContent from '../collectPageContent.js';
|
|
16
19
|
import writeJs from '../buildJs/writeJs.js';
|
|
17
20
|
async function writePageJit({ page, context }) {
|
|
18
21
|
// Write page JSON
|
|
@@ -40,5 +43,10 @@ async function writePageJit({ page, context }) {
|
|
|
40
43
|
await writeJs({
|
|
41
44
|
context
|
|
42
45
|
});
|
|
46
|
+
// Write per-page content file for Tailwind to scan class and property strings
|
|
47
|
+
const pageContent = collectPageContent([
|
|
48
|
+
page
|
|
49
|
+
]);
|
|
50
|
+
await writeFile(path.join(context.directories.server, 'lowdefy-build', 'tailwind', `${page.pageId}.html`), '<!-- Generated by Lowdefy build -->\n' + (pageContent ?? ''));
|
|
43
51
|
}
|
|
44
52
|
export default writePageJit;
|