@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.
Files changed (56) hide show
  1. package/dist/build/addDefaultPages/404.js +8 -2
  2. package/dist/build/buildApi/buildRoutine/validateStep.js +7 -5
  3. package/dist/build/buildApi/validateEndpoint.js +6 -5
  4. package/dist/build/buildConnections.js +6 -0
  5. package/dist/build/buildImports/buildIconImports.js +5 -1
  6. package/dist/build/buildImports/buildImportsDev.js +1 -6
  7. package/dist/build/buildImports/buildImportsProd.js +1 -6
  8. package/dist/build/buildImports/validateIconImports.js +65 -0
  9. package/dist/build/buildJs/jsMapParser.js +5 -2
  10. package/dist/build/buildPages/buildBlock/buildBlock.js +12 -4
  11. package/dist/build/buildPages/buildBlock/buildEvents.js +34 -1
  12. package/dist/build/buildPages/buildBlock/buildRequests.js +7 -5
  13. package/dist/build/buildPages/buildBlock/buildSubBlocks.js +9 -9
  14. package/dist/build/buildPages/buildBlock/countBlockOperators.js +1 -1
  15. package/dist/build/buildPages/buildBlock/moveAreasToSlots.js +31 -0
  16. package/dist/build/buildPages/buildBlock/{moveSkeletonBlocksToArea.js → moveSkeletonBlocksToSlot.js} +8 -8
  17. package/dist/build/buildPages/buildBlock/{moveSubBlocksToArea.js → moveSubBlocksToSlot.js} +3 -3
  18. package/dist/build/buildPages/buildBlock/normalizeClassAndStyles.js +124 -0
  19. package/dist/build/buildPages/buildBlock/normalizeLayout.js +68 -0
  20. package/dist/build/buildPages/buildBlock/setBlockId.js +7 -1
  21. package/dist/build/buildPages/buildBlock/validateSlots.js +34 -0
  22. package/dist/build/buildPages/buildPage.js +23 -1
  23. package/dist/build/buildRefs/addLineNumbers.js +76 -0
  24. package/dist/build/{buildImports/buildStyleImports.js → buildRefs/getLineNumber.js} +4 -10
  25. package/dist/build/buildRefs/getRefContent.js +9 -1
  26. package/dist/build/buildRefs/parseRefContent.js +4 -66
  27. package/dist/build/buildTypes.js +4 -2
  28. package/dist/build/cleanBuildDirectory.js +3 -1
  29. package/dist/build/collectPageContent.js +57 -0
  30. package/dist/build/jit/buildPageJit.js +14 -3
  31. package/dist/build/jit/extractIconData.js +16 -1
  32. package/dist/build/jit/pageContentKeys.js +1 -0
  33. package/dist/build/jit/shallowBuild.js +24 -0
  34. package/dist/build/jit/stripPageContent.js +29 -0
  35. package/dist/build/jit/writePageJit.js +9 -1
  36. package/dist/build/testSchema.js +3 -0
  37. package/dist/build/writePluginImports/collectBlockSourceContent.js +65 -0
  38. package/dist/build/writePluginImports/writeActionSchemaMap.js +1 -1
  39. package/dist/build/writePluginImports/writeBlockSchemaMap.js +45 -7
  40. package/dist/build/writePluginImports/writeGlobalsCss.js +126 -0
  41. package/dist/build/writePluginImports/writeOperatorSchemaMap.js +1 -1
  42. package/dist/build/writePluginImports/writePluginImports.js +7 -2
  43. package/dist/build/writeTheme.js +28 -0
  44. package/dist/createContext.js +2 -0
  45. package/dist/defaultTypesMap.js +1693 -837
  46. package/dist/index.js +16 -0
  47. package/dist/lowdefySchema.js +100 -0
  48. package/dist/scripts/generateDefaultTypes.js +5 -10
  49. package/dist/test-utils/runBuild.js +3 -0
  50. package/dist/test-utils/runBuildForSnapshots.js +5 -2
  51. package/dist/test-utils/testContext.js +2 -1
  52. package/dist/utils/createHandleWarning.js +3 -0
  53. package/dist/utils/createPluginTypesMap.js +5 -9
  54. package/dist/utils/validateId.js +24 -0
  55. package/package.json +47 -47
  56. package/dist/build/writePluginImports/writeStyleImports.js +0 -34
@@ -48,9 +48,12 @@ function testSchema({ components, context }) {
48
48
  components,
49
49
  instancePath
50
50
  });
51
+ const propertyName = instancePath[instancePath.length - 1];
51
52
  let message = error.message;
52
53
  if (error.params?.additionalProperty) {
53
54
  message = `${message} - "${error.params.additionalProperty}"`;
55
+ } else if (propertyName) {
56
+ message = `"${propertyName}" ${message}`;
54
57
  }
55
58
  const configError = new ConfigError(message, {
56
59
  configKey,
@@ -0,0 +1,65 @@
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 fs from 'fs';
16
+ import path from 'path';
17
+ import { createRequire } from 'node:module';
18
+ // Extract all string literals from JS source that could be Tailwind class candidates.
19
+ // We read the raw JS text — no AST parsing needed. Tailwind's scanner does the same:
20
+ // it treats every word-like token as a potential class candidate.
21
+ function readJsFiles(distDir) {
22
+ const content = [];
23
+ function walk(dir) {
24
+ for (const entry of fs.readdirSync(dir, {
25
+ withFileTypes: true
26
+ })){
27
+ const fullPath = path.join(dir, entry.name);
28
+ if (entry.isDirectory()) {
29
+ walk(fullPath);
30
+ } else if (/\.(js|jsx|ts|tsx|mjs)$/.test(entry.name)) {
31
+ content.push(fs.readFileSync(fullPath, 'utf8'));
32
+ }
33
+ }
34
+ }
35
+ if (fs.existsSync(distDir)) {
36
+ walk(distDir);
37
+ }
38
+ return content.join('\n');
39
+ }
40
+ function collectBlockSourceContent({ components, serverDirectory }) {
41
+ // Resolve packages from the server directory, not from @lowdefy/build's own location.
42
+ // In production, @lowdefy/build is installed inside .lowdefy/server/node_modules/.
43
+ // With pnpm, import.meta.url resolves to the .pnpm store where only @lowdefy/build's
44
+ // own declared deps are visible. Block packages are deps of the server, not of
45
+ // @lowdefy/build, so require.resolve from import.meta.url silently fails.
46
+ // Rooting at the server's package.json ensures block packages are always reachable.
47
+ const requireFromServer = createRequire(path.join(serverDirectory, 'package.json'));
48
+ const packages = [
49
+ ...new Set((components.imports?.blocks ?? []).map((b)=>b.package))
50
+ ];
51
+ const allContent = [];
52
+ for (const packageName of packages){
53
+ try {
54
+ // Resolve the package entry to find its real location on disk.
55
+ // This follows pnpm symlinks, yarn PnP, npm hoisting — any install strategy.
56
+ const entryPath = requireFromServer.resolve(`${packageName}/blocks`);
57
+ const distDir = path.dirname(entryPath);
58
+ allContent.push(readJsFiles(distDir));
59
+ } catch {
60
+ // Package not resolvable from server context (e.g., custom plugin not installed) — skip.
61
+ }
62
+ }
63
+ return allContent.join('\n');
64
+ }
65
+ export default collectBlockSourceContent;
@@ -25,7 +25,7 @@
25
25
  for (const [packageName, actions] of Object.entries(actionsByPackage)){
26
26
  let packageSchemas;
27
27
  try {
28
- packageSchemas = await import(`${packageName}/schemas`);
28
+ packageSchemas = await import(/* webpackIgnore: true */ `${packageName}/schemas`);
29
29
  } catch {
30
30
  // Package not resolvable from build context (custom plugins) — skip
31
31
  }
@@ -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
- */ async function writeBlockSchemaMap({ components, context }) {
15
+ */ import { buildBlockSchema } from '@lowdefy/block-utils';
16
+ async function writeBlockSchemaMap({ components, context }) {
16
17
  const schemas = {};
18
+ const allMetas = {};
17
19
  const typesMapSchemas = context.typesMap.schemas?.blocks ?? {};
18
20
  const blocksByPackage = {};
19
21
  for (const block of components.imports.blocks){
@@ -23,20 +25,56 @@
23
25
  blocksByPackage[block.package].push(block);
24
26
  }
25
27
  for (const [packageName, blocks] of Object.entries(blocksByPackage)){
26
- let packageSchemas;
28
+ let packageMetas;
27
29
  try {
28
- packageSchemas = await import(`${packageName}/schemas`);
30
+ packageMetas = await import(/* webpackIgnore: true */ `${packageName}/metas`);
29
31
  } catch {
30
- // Package not resolvable from build context (custom plugins) — skip
32
+ try {
33
+ packageMetas = await import(/* webpackIgnore: true */ `${packageName}/schemas`);
34
+ } catch {
35
+ // Package not resolvable from build context (custom plugins) — skip
36
+ }
31
37
  }
32
38
  for (const block of blocks){
39
+ const meta = packageMetas?.[block.originalTypeName];
33
40
  if (typesMapSchemas[block.typeName]) {
34
41
  schemas[block.typeName] = typesMapSchemas[block.typeName];
35
- } else if (packageSchemas?.[block.originalTypeName]) {
36
- schemas[block.typeName] = packageSchemas[block.originalTypeName];
42
+ } else if (meta) {
43
+ schemas[block.typeName] = buildBlockSchema(meta);
44
+ }
45
+ if (meta) {
46
+ allMetas[block.typeName] = meta;
37
47
  }
38
48
  }
39
49
  }
40
- return context.writeBuildArtifact('plugins/blockSchemas.json', JSON.stringify(schemas));
50
+ const blockMetas = {};
51
+ const typesMapBlockMetas = context.typesMap.blockMetas ?? {};
52
+ for (const block of components.imports.blocks){
53
+ const typesMapMeta = typesMapBlockMetas[block.typeName];
54
+ const meta = allMetas[block.typeName];
55
+ if (typesMapMeta) {
56
+ blockMetas[block.typeName] = {
57
+ category: typesMapMeta.category,
58
+ ...typesMapMeta.valueType != null && {
59
+ valueType: typesMapMeta.valueType
60
+ },
61
+ ...typesMapMeta.initValue !== undefined && {
62
+ initValue: typesMapMeta.initValue
63
+ }
64
+ };
65
+ } else if (meta) {
66
+ blockMetas[block.typeName] = {
67
+ category: meta.category,
68
+ ...meta.valueType != null && {
69
+ valueType: meta.valueType
70
+ },
71
+ ...meta.initValue !== undefined && {
72
+ initValue: meta.initValue
73
+ }
74
+ };
75
+ }
76
+ }
77
+ await context.writeBuildArtifact('plugins/blockSchemas.json', JSON.stringify(schemas));
78
+ await context.writeBuildArtifact('plugins/blockMetas.json', JSON.stringify(blockMetas));
41
79
  }
42
80
  export default writeBlockSchemaMap;
@@ -0,0 +1,126 @@
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 fs from 'fs';
16
+ import path from 'path';
17
+ import { ConfigError } from '@lowdefy/errors';
18
+ import { mergeObjects } from '@lowdefy/helpers';
19
+ import { writeFile } from '@lowdefy/node-utils';
20
+ import collectBlockSourceContent from './collectBlockSourceContent.js';
21
+ const BRIDGE_DEFAULTS = {
22
+ color: {
23
+ primary: 'var(--ant-color-primary)',
24
+ 'primary-hover': 'var(--ant-color-primary-hover)',
25
+ 'primary-active': 'var(--ant-color-primary-active)',
26
+ 'primary-bg': 'var(--ant-color-primary-bg)',
27
+ success: 'var(--ant-color-success)',
28
+ warning: 'var(--ant-color-warning)',
29
+ error: 'var(--ant-color-error)',
30
+ info: 'var(--ant-color-info)',
31
+ 'text-primary': 'var(--ant-color-text)',
32
+ 'text-secondary': 'var(--ant-color-text-secondary)',
33
+ 'bg-container': 'var(--ant-color-bg-container)',
34
+ 'bg-layout': 'var(--ant-color-bg-layout)',
35
+ border: 'var(--ant-color-border)'
36
+ },
37
+ radius: {
38
+ DEFAULT: 'var(--ant-border-radius)',
39
+ sm: 'var(--ant-border-radius-sm)',
40
+ lg: 'var(--ant-border-radius-lg)'
41
+ },
42
+ 'font-size': {
43
+ DEFAULT: 'var(--ant-font-size)',
44
+ sm: 'var(--ant-font-size-sm)',
45
+ lg: 'var(--ant-font-size-lg)'
46
+ },
47
+ 'font-family': {
48
+ sans: 'var(--ant-font-family)'
49
+ }
50
+ };
51
+ function objectToThemeVars(obj, prefix) {
52
+ const lines = [];
53
+ for (const [key, value] of Object.entries(obj)){
54
+ const varName = prefix ? `${prefix}-${key}` : `--${key}`;
55
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
56
+ lines.push(...objectToThemeVars(value, varName));
57
+ } else {
58
+ lines.push(` ${varName}: ${value};`);
59
+ }
60
+ }
61
+ return lines;
62
+ }
63
+ function buildThemeVars(tailwindConfig) {
64
+ const merged = mergeObjects([
65
+ {},
66
+ BRIDGE_DEFAULTS,
67
+ tailwindConfig ?? {}
68
+ ]);
69
+ return objectToThemeVars(merged).join('\n');
70
+ }
71
+ async function writeGlobalsCss({ components, context }) {
72
+ if (fs.existsSync(path.join(context.directories.config, 'public/styles.less'))) {
73
+ throw new ConfigError('public/styles.less is deprecated. Migrate to: (1) "theme" key in lowdefy.yaml for token overrides (recommended), (2) public/styles.css for custom CSS.');
74
+ }
75
+ const tailwindConfig = components.theme?.tailwind;
76
+ const themeVars = buildThemeVars(tailwindConfig);
77
+ const userStylesAbsolute = path.join(context.directories.config, 'public/styles.css');
78
+ const importUserStyles = fs.existsSync(userStylesAbsolute);
79
+ let userStylesImport = '';
80
+ if (importUserStyles) {
81
+ const relPath = path.relative(context.directories.build, userStylesAbsolute).split(path.sep).join('/');
82
+ userStylesImport = `/* User custom styles */\n@import "${relPath}" layer(components);\n\n`;
83
+ }
84
+ const css = `/* Generated by Lowdefy build */
85
+
86
+ /* Layer order — locks cascade priority before Tailwind declares its own layers */
87
+ @layer theme, base, antd, components, utilities;
88
+
89
+ @import "tailwindcss";
90
+ @import "@lowdefy/layout/grid.css";
91
+
92
+ ${userStylesImport}/* Content sources for Tailwind JIT — block JS content collected at build time */
93
+ @source "../lowdefy-build/tailwind/*.html";
94
+
95
+ /* Imported CSS file — when this changes, PostCSS re-runs and Tailwind re-scans @source */
96
+ @import "./tailwind-candidates.css";
97
+
98
+ /* Antd-to-Tailwind theme bridge — extends default Tailwind theme with antd design tokens */
99
+ @theme inline {
100
+ ${themeVars}
101
+ }
102
+ `;
103
+ await context.writeBuildArtifact('globals.css', css);
104
+ // Standalone layer order declaration — imported FIRST in _app.js so that
105
+ // Next.js/Turbopack generates a critical CSS chunk that loads before any
106
+ // JavaScript (including antd's CSS-in-JS). This guarantees the cascade
107
+ // layer priority is locked (antd > base/preflight) before antd injects
108
+ // @layer antd {} at runtime. Without this, antd wins the race and becomes
109
+ // the lowest-priority layer.
110
+ await context.writeBuildArtifact('layer-order.css', '/* Generated by Lowdefy build */\n@layer theme, base, antd, components, utilities;\n');
111
+ await context.writeBuildArtifact('tailwind-candidates.css', '/* Generated by Lowdefy build — rewritten on page changes to trigger CSS recompilation */\n');
112
+ for (const [pageId, content] of context.tailwindContentMap ?? []){
113
+ await writeFile(path.join(context.directories.server, 'lowdefy-build', 'tailwind', `${pageId}.html`), '<!-- Generated by Lowdefy build -->\n' + content);
114
+ }
115
+ // Collect Tailwind class candidates from block plugin JS source files.
116
+ // Resolves from the server directory so block packages are reachable regardless
117
+ // of package manager (pnpm strict isolation, yarn PnP, npm hoisting).
118
+ const blockContent = collectBlockSourceContent({
119
+ components,
120
+ serverDirectory: context.directories.server
121
+ });
122
+ if (blockContent) {
123
+ await writeFile(path.join(context.directories.server, 'lowdefy-build', 'tailwind', '_blocks.html'), '<!-- Block plugin JS content collected at build time -->\n' + blockContent);
124
+ }
125
+ }
126
+ export default writeGlobalsCss;
@@ -32,7 +32,7 @@
32
32
  for (const [packageName, operators] of Object.entries(operatorsByPackage)){
33
33
  let packageSchemas;
34
34
  try {
35
- packageSchemas = await import(`${packageName}/schemas`);
35
+ packageSchemas = await import(/* webpackIgnore: true */ `${packageName}/schemas`);
36
36
  } catch {
37
37
  // Package not resolvable from build context (custom plugins) — skip
38
38
  }
@@ -21,7 +21,7 @@ import writeConnectionImports from './writeConnectionImports.js';
21
21
  import writeIconImports from './writeIconImports.js';
22
22
  import writeOperatorImports from './writeOperatorImports.js';
23
23
  import writeOperatorSchemaMap from './writeOperatorSchemaMap.js';
24
- import writeStyleImports from './writeStyleImports.js';
24
+ import writeGlobalsCss from './writeGlobalsCss.js';
25
25
  async function writePluginImports({ components, context }) {
26
26
  await writeActionImports({
27
27
  components,
@@ -59,9 +59,14 @@ async function writePluginImports({ components, context }) {
59
59
  components,
60
60
  context
61
61
  });
62
- await writeStyleImports({
62
+ await writeGlobalsCss({
63
63
  components,
64
64
  context
65
65
  });
66
+ // Write block package names for Next.js transpilePackages (CSS imports).
67
+ const blockPackages = [
68
+ ...new Set((components.imports.blocks ?? []).map((b)=>b.package))
69
+ ];
70
+ await context.writeBuildArtifact('blockPackages.json', JSON.stringify(blockPackages));
66
71
  }
67
72
  export default writePluginImports;
@@ -0,0 +1,28 @@
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, serializer } from '@lowdefy/helpers';
16
+ async function writeTheme({ components, context }) {
17
+ if (type.isNone(components.theme)) {
18
+ components.theme = {};
19
+ }
20
+ if (!type.isObject(components.theme)) {
21
+ throw new Error('Theme is not an object.');
22
+ }
23
+ if (type.isNone(components.theme.darkMode)) {
24
+ components.theme.darkMode = 'system';
25
+ }
26
+ await context.writeBuildArtifact('theme.json', serializer.serializeToString(components.theme));
27
+ }
28
+ export default writeTheme;
@@ -25,6 +25,7 @@ function createContext({ customTypesMap, directories, logger, refResolver, stage
25
25
  directories,
26
26
  errors: [],
27
27
  jsMap: {},
28
+ warnings: [],
28
29
  keyMap: {},
29
30
  logger,
30
31
  readConfigFile: createReadConfigFile({
@@ -60,6 +61,7 @@ function createContext({ customTypesMap, directories, logger, refResolver, stage
60
61
  directories
61
62
  })
62
63
  };
64
+ context.blockMetas = context.typesMap.blockMetas ?? {};
63
65
  context.handleError = createBuildHandleError({
64
66
  context
65
67
  });