@lowdefy/build 0.0.0-experimental-20251203181051 → 0.0.0-experimental-20251203191458

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.
@@ -12,7 +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
- */ const iconPackages = {
15
+ */ import createBuildProfiler from '../../utils/createBuildProfiler.js';
16
+ const iconPackages = {
16
17
  'react-icons/ai': /"(Ai[A-Z0-9]\w*)"/gm,
17
18
  'react-icons/bi': /"(Bi[A-Z0-9]\w*)"/gm,
18
19
  'react-icons/bs': /"(Bs[A-Z0-9]\w*)"/gm,
@@ -42,27 +43,40 @@
42
43
  'react-icons/vsc': /"(Vsc[A-Z0-9]\w*)"/gm,
43
44
  'react-icons/wi': /"(Wi[A-Z0-9]\w*)"/gm
44
45
  };
45
- function getConfigIcons({ components, icons, regex }) {
46
- [
47
- ...JSON.stringify(components.global || {}).matchAll(regex)
48
- ].map((match)=>icons.add(match[1]));
49
- [
50
- ...JSON.stringify(components.menus || []).matchAll(regex)
51
- ].map((match)=>icons.add(match[1]));
52
- [
53
- ...JSON.stringify(components.pages || []).matchAll(regex)
54
- ].map((match)=>icons.add(match[1]));
46
+ function getConfigIcons({ components, icons, regex, timeSync }) {
47
+ timeSync('stringify:global', ()=>{
48
+ [
49
+ ...JSON.stringify(components.global || {}).matchAll(regex)
50
+ ].map((match)=>icons.add(match[1]));
51
+ });
52
+ timeSync('stringify:menus', ()=>{
53
+ [
54
+ ...JSON.stringify(components.menus || []).matchAll(regex)
55
+ ].map((match)=>icons.add(match[1]));
56
+ });
57
+ timeSync('stringify:pages', ()=>{
58
+ [
59
+ ...JSON.stringify(components.pages || []).matchAll(regex)
60
+ ].map((match)=>icons.add(match[1]));
61
+ });
55
62
  }
56
- function getBlockDefaultIcons({ blocks, context, icons, regex }) {
57
- blocks.forEach((block)=>{
58
- (context.typesMap.icons[block.typeName] || []).forEach((icon)=>{
59
- [
60
- ...JSON.stringify(icon).matchAll(regex)
61
- ].map((match)=>icons.add(match[1]));
63
+ function getBlockDefaultIcons({ blocks, context, icons, regex, timeSync }) {
64
+ timeSync('blockDefaultIcons', ()=>{
65
+ blocks.forEach((block)=>{
66
+ (context.typesMap.icons[block.typeName] || []).forEach((icon)=>{
67
+ [
68
+ ...JSON.stringify(icon).matchAll(regex)
69
+ ].map((match)=>icons.add(match[1]));
70
+ });
62
71
  });
63
72
  });
64
73
  }
65
74
  function buildIconImports({ blocks, components, context, defaults = {} }) {
75
+ const profiler = createBuildProfiler({
76
+ logger: context.logger,
77
+ prefix: 'buildIconImports'
78
+ });
79
+ const timeSync = profiler.timeSync;
66
80
  const iconImports = [];
67
81
  Object.entries(iconPackages).forEach(([iconPackage, regex])=>{
68
82
  defaults;
@@ -70,13 +84,15 @@ function buildIconImports({ blocks, components, context, defaults = {} }) {
70
84
  getConfigIcons({
71
85
  components,
72
86
  icons,
73
- regex
87
+ regex,
88
+ timeSync
74
89
  });
75
90
  getBlockDefaultIcons({
76
91
  blocks,
77
92
  context,
78
93
  icons,
79
- regex
94
+ regex,
95
+ timeSync
80
96
  });
81
97
  iconImports.push({
82
98
  icons: [
@@ -85,6 +101,7 @@ function buildIconImports({ blocks, components, context, defaults = {} }) {
85
101
  package: iconPackage
86
102
  });
87
103
  });
104
+ profiler.printSummary();
88
105
  return iconImports;
89
106
  }
90
107
  export default buildIconImports;
@@ -12,23 +12,30 @@
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 recursiveBuild from './recursiveBuild.js';
15
+ */ import createBuildProfiler from '../../utils/createBuildProfiler.js';
16
+ import recursiveBuild from './recursiveBuild.js';
16
17
  import makeRefDefinition from './makeRefDefinition.js';
17
18
  import evaluateBuildOperators from './evaluateBuildOperators.js';
18
19
  async function buildRefs({ context }) {
20
+ const profiler = createBuildProfiler({
21
+ logger: context.logger,
22
+ prefix: 'buildRefs'
23
+ });
19
24
  const refDef = makeRefDefinition('lowdefy.yaml', null, context.refMap);
20
25
  const refCache = new Map();
21
- let components = await recursiveBuild({
22
- context,
23
- refDef,
24
- count: 0,
25
- refCache
26
- });
27
- components = await evaluateBuildOperators({
28
- context,
29
- input: components,
30
- refDef
31
- });
26
+ let components = await profiler.time('recursiveBuild', ()=>recursiveBuild({
27
+ context,
28
+ refDef,
29
+ count: 0,
30
+ refCache,
31
+ profiler
32
+ }));
33
+ components = await profiler.time('evaluateBuildOperators:final', ()=>evaluateBuildOperators({
34
+ context,
35
+ input: components,
36
+ refDef
37
+ }));
38
+ profiler.printSummary();
32
39
  return components ?? {};
33
40
  }
34
41
  export default buildRefs;
@@ -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
- */ import { BuildParser } from '@lowdefy/operators';
16
- import operators from '@lowdefy/operators-js/operators/build';
17
- async function evaluateBuildOperators({ context, input, refDef }) {
18
- const operatorsParser = new BuildParser({
19
- env: process.env,
20
- operators
21
- });
22
- const { output, errors } = operatorsParser.parse({
15
+ */ async function evaluateBuildOperators({ context, input, refDef }) {
16
+ const { output, errors } = context.operatorsParser.parse({
23
17
  input,
24
18
  location: refDef.path ?? refDef.resolver,
25
19
  operatorPrefix: '_build.'
@@ -12,27 +12,47 @@
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 getConfigFile from './getConfigFile.js';
15
+ */ import { getFileExtension } from '@lowdefy/node-utils';
16
+ import getConfigFile from './getConfigFile.js';
16
17
  import parseRefContent from './parseRefContent.js';
17
18
  import runRefResolver from './runRefResolver.js';
19
+ function getCacheKey(refDef) {
20
+ const { path, vars } = refDef;
21
+ // For nunjucks files, vars affect the output, so include them in the cache key
22
+ const ext = getFileExtension(path);
23
+ if (ext === 'njk' && vars) {
24
+ return `${path}::${JSON.stringify(vars)}`;
25
+ }
26
+ return path;
27
+ }
18
28
  async function getRefContent({ context, refDef, referencedFrom }) {
19
- let content;
29
+ // Skip caching for resolver-based refs (dynamic content)
20
30
  if (refDef.resolver || context.refResolver) {
21
- content = await runRefResolver({
31
+ const content = await runRefResolver({
22
32
  context,
23
33
  refDef,
24
34
  referencedFrom
25
35
  });
26
- } else {
27
- content = await getConfigFile({
28
- context,
29
- refDef,
30
- referencedFrom
36
+ return parseRefContent({
37
+ content,
38
+ refDef
31
39
  });
32
40
  }
33
- return parseRefContent({
34
- content,
41
+ const cacheKey = getCacheKey(refDef);
42
+ const cached = context.parsedContentCache.get(cacheKey);
43
+ if (cached !== undefined) {
44
+ return cached;
45
+ }
46
+ const rawContent = await getConfigFile({
47
+ context,
48
+ refDef,
49
+ referencedFrom
50
+ });
51
+ const parsed = parseRefContent({
52
+ content: rawContent,
35
53
  refDef
36
54
  });
55
+ context.parsedContentCache.set(cacheKey, parsed);
56
+ return parsed;
37
57
  }
38
58
  export default getRefContent;
@@ -19,7 +19,9 @@ import getRefContent from './getRefContent.js';
19
19
  import getRefsFromFile from './getRefsFromFile.js';
20
20
  import populateRefs from './populateRefs.js';
21
21
  import runTransformer from './runTransformer.js';
22
- async function recursiveBuild({ context, refDef, count, referencedFrom, refCache }) {
22
+ async function recursiveBuild({ context, refDef, count, referencedFrom, refCache, profiler }) {
23
+ const time = profiler?.time ?? ((_, fn)=>fn());
24
+ const timeSync = profiler?.timeSync ?? ((_, fn)=>fn());
23
25
  // TODO: Maybe it would be better to detect a cycle, since this is the real issue here?
24
26
  if (count > 10000) {
25
27
  throw new Error(`Maximum recursion depth of references exceeded.`);
@@ -27,63 +29,66 @@ async function recursiveBuild({ context, refDef, count, referencedFrom, refCache
27
29
  if (refCache.has(refDef.hash)) {
28
30
  return refCache.get(refDef.hash);
29
31
  }
30
- let fileContent = await getRefContent({
31
- context,
32
- refDef,
33
- referencedFrom
34
- });
35
- const { foundRefs, fileContentBuiltRefs } = getRefsFromFile(fileContent, refDef.hash, context.refMap);
32
+ let fileContent = await time('getRefContent', ()=>getRefContent({
33
+ context,
34
+ refDef,
35
+ referencedFrom
36
+ }));
37
+ const { foundRefs, fileContentBuiltRefs } = timeSync('getRefsFromFile', ()=>getRefsFromFile(fileContent, refDef.hash, context.refMap));
36
38
  // Since we can have references in the variables of a reference, we need to first parse
37
39
  // the deeper nodes, so we can use those parsed files in references higher in the tree.
38
40
  // To do this, since foundRefs is an array of ref definitions that are in order of the
39
41
  // deepest nodes first we for loop over over foundRefs one by one, awaiting each result.
40
42
  for (const newRefDef of foundRefs.values()){
41
43
  // Parse vars and path before passing down to parse new file
42
- const parsedRefDef = populateRefs({
43
- toPopulate: newRefDef,
44
- refCache,
45
- refDef
46
- });
44
+ const parsedRefDef = timeSync('populateRefs:vars', ()=>populateRefs({
45
+ toPopulate: newRefDef,
46
+ refCache,
47
+ refDef
48
+ }));
47
49
  context.refMap[parsedRefDef.hash].path = parsedRefDef.path;
48
50
  const parsedFile = await recursiveBuild({
49
51
  context,
50
52
  refDef: parsedRefDef,
51
53
  count: count + 1,
52
54
  referencedFrom: refDef.path,
53
- refCache
54
- });
55
- const transformedFile = await runTransformer({
56
- context,
57
- input: parsedFile,
58
- refDef: parsedRefDef
55
+ refCache,
56
+ profiler
59
57
  });
58
+ const transformedFile = await time('runTransformer', ()=>runTransformer({
59
+ context,
60
+ input: parsedFile,
61
+ refDef: parsedRefDef
62
+ }));
60
63
  // Evaluated in recursive loop for better error messages
61
- const evaluatedOperators = await evaluateBuildOperators({
62
- context,
63
- input: transformedFile,
64
- refDef: parsedRefDef
65
- });
66
- const withRefKey = getKey({
67
- input: evaluatedOperators,
68
- refDef: parsedRefDef
64
+ const evaluatedOperators = await time('evaluateBuildOperators', ()=>evaluateBuildOperators({
65
+ context,
66
+ input: transformedFile,
67
+ refDef: parsedRefDef
68
+ }));
69
+ const withRefKey = timeSync('getKey', ()=>getKey({
70
+ input: evaluatedOperators,
71
+ refDef: parsedRefDef
72
+ }));
73
+ timeSync('cacheWithReviver', ()=>{
74
+ const reviver = (_, value)=>{
75
+ if (!type.isObject(value)) return value;
76
+ Object.defineProperty(value, '~r', {
77
+ value: refDef.hash,
78
+ enumerable: false,
79
+ writable: true,
80
+ configurable: true
81
+ });
82
+ return value;
83
+ };
84
+ refCache.set(newRefDef.hash, JSON.parse(JSON.stringify(withRefKey), reviver));
69
85
  });
70
- const reviver = (_, value)=>{
71
- if (!type.isObject(value)) return value;
72
- Object.defineProperty(value, '~r', {
73
- value: refDef.hash,
74
- enumerable: false,
75
- writable: true,
76
- configurable: true
77
- });
78
- return value;
79
- };
80
- refCache.set(newRefDef.hash, JSON.parse(JSON.stringify(withRefKey), reviver));
81
86
  }
82
- const result = populateRefs({
83
- toPopulate: fileContentBuiltRefs,
84
- refCache,
85
- refDef
86
- });
87
+ const result = timeSync('populateRefs:final', ()=>populateRefs({
88
+ toPopulate: fileContentBuiltRefs,
89
+ refCache,
90
+ refDef
91
+ }));
87
92
  refCache.set(refDef.hash, result);
88
93
  return result;
89
94
  }
@@ -13,17 +13,25 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */ import { mergeObjects } from '@lowdefy/helpers';
16
+ import { BuildParser } from '@lowdefy/operators';
17
+ import operators from '@lowdefy/operators-js/operators/build';
16
18
  import createCounter from './utils/createCounter.js';
17
19
  import createReadConfigFile from './utils/readConfigFile.js';
18
20
  import createWriteBuildArtifact from './utils/writeBuildArtifact.js';
19
21
  import defaultTypesMap from './defaultTypesMap.js';
20
22
  function createContext({ customTypesMap, directories, entitlements = [], logger, refResolver, stage = 'prod' }) {
23
+ const operatorsParser = new BuildParser({
24
+ env: process.env,
25
+ operators
26
+ });
21
27
  const context = {
22
28
  directories,
23
29
  entitlements,
24
30
  jsMap: {},
25
31
  keyMap: {},
26
32
  logger,
33
+ operatorsParser,
34
+ parsedContentCache: new Map(),
27
35
  readConfigFile: createReadConfigFile({
28
36
  directories
29
37
  }),