@lowdefy/build
Version:
97 lines (93 loc) • 3.93 kB
JavaScript
/* eslint-disable no-param-reassign */ /*
Copyright 2020-2026 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/ import { type } from '@lowdefy/helpers';
import { ConfigError, shouldSuppressBuildCheck } from '@lowdefy/errors';
import buildPage from '../buildPages/buildPage.js';
import createCheckDuplicateId from '../../utils/createCheckDuplicateId.js';
import validateCallApiRefs from '../buildPages/validateCallApiRefs.js';
import validateLinkReferences from '../buildPages/validateLinkReferences.js';
import validatePayloadReferences from '../buildPages/validatePayloadReferences.js';
import validateServerStateReferences from '../buildPages/validateServerStateReferences.js';
import validateStateReferences from '../buildPages/validateStateReferences.js';
function buildPages({ components, context }) {
const pages = type.isArray(components.pages) ? components.pages : [];
const checkDuplicatePageId = createCheckDuplicateId({
message: 'Duplicate pageId "{{ id }}".'
});
// Initialize action ref collections across all pages
context.linkActionRefs = [];
context.callApiActionRefs = [];
// Track which pages failed to build so we skip them in validation
const failedPageIndices = new Set();
// Wrap each page build to collect errors instead of stopping on first error
pages.forEach((page, index)=>{
try {
const result = buildPage({
page,
index,
context,
checkDuplicatePageId
});
// buildPage returns { failed: true } when validation fails
if (result?.failed) {
failedPageIndices.add(index);
}
} catch (error) {
// Skip suppressed ConfigErrors (via ~ignoreBuildChecks)
if (error instanceof ConfigError && shouldSuppressBuildCheck(error, context.keyMap)) {
return;
}
// Collect error object if context.errors exists, otherwise throw (for backward compat with tests)
if (context?.errors) {
context.errors.push(error);
failedPageIndices.add(index);
} else {
throw error;
}
}
});
// Validate that all Link actions reference existing pages
// Include all pages — a link to a broken page is valid; the page error is already reported
const pageIds = pages.map((page)=>page.pageId);
validateLinkReferences({
linkActionRefs: context.linkActionRefs,
pageIds,
context
});
// Validate that CallAPI actions don't target InternalApi endpoints
const endpointConfigs = type.isArray(components.api) ? components.api : [];
validateCallApiRefs({
callApiActionRefs: context.callApiActionRefs,
endpointConfigs,
context
});
// Validate that _state references use defined block IDs
// and _payload references use defined payload keys
// Skip pages that failed to build
pages.forEach((page, index)=>{
if (failedPageIndices.has(index)) return;
validateStateReferences({
page,
context
});
validatePayloadReferences({
page,
context
});
validateServerStateReferences({
page,
context
});
});
return components;
}
export default buildPages;