generated from polymech/site-template
config | pm-astro cli
This commit is contained in:
parent
a59625c4b1
commit
3da1040d72
@ -23,7 +23,7 @@
|
||||
"format": "unix-time"
|
||||
}
|
||||
],
|
||||
"default": "2025-12-27T17:43:28.651Z"
|
||||
"default": "2025-12-27T18:35:25.856Z"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -143,6 +143,7 @@
|
||||
"i18n": {
|
||||
"store": "${OSR_ROOT}/i18n-store/store-${LANG}.json",
|
||||
"cache": true,
|
||||
"source_language": "en",
|
||||
"asset_path": "${SRC_DIR}/${SRC_NAME}-${DST_LANG}${SRC_EXT}"
|
||||
},
|
||||
"products": {
|
||||
|
||||
@ -10,15 +10,14 @@ import getReadingTime from 'reading-time';
|
||||
import { toString } from 'mdast-util-to-string';
|
||||
import { PolymechInstance } from "@polymech/astro-base/registry";
|
||||
|
||||
import { loadConfig } from './src/app/config-loader.js';
|
||||
import { I18N_SOURCE_LANGUAGE } from "./src/app/constants.js"
|
||||
import { loadConfig } from '@polymech/astro-base/app/config-loader';
|
||||
|
||||
// import domainExpansion from '@domain-expansion/astro';
|
||||
import yargs from 'yargs'
|
||||
import { hideBin } from 'yargs/helpers'
|
||||
|
||||
const argv = yargs(hideBin(process.argv)).argv;
|
||||
const config = loadConfig(I18N_SOURCE_LANGUAGE);
|
||||
const config = loadConfig();
|
||||
|
||||
PolymechInstance.setConfig({
|
||||
...config,
|
||||
|
||||
5
package-lock.json
generated
5
package-lock.json
generated
@ -224,6 +224,7 @@
|
||||
"@polymech/i18n": "file:../../../polymech-mono/packages/i18n",
|
||||
"@polymech/kbot-d": "file:../../../polymech-mono/packages/kbot",
|
||||
"@polymech/log": "file:../../../polymech-mono/packages/log",
|
||||
"@types/yargs": "^17.0.35",
|
||||
"astro": "^5.13.2",
|
||||
"exifreader": "^4.31.1",
|
||||
"find-up": "^7.0.0",
|
||||
@ -237,6 +238,7 @@
|
||||
"mdast-util-to-string": "^4.0.0",
|
||||
"node-xlsx": "^0.24.0",
|
||||
"p-map": "^7.0.3",
|
||||
"quicktype-core": "^23.2.6",
|
||||
"react-jsx-parser": "^2.4.0",
|
||||
"reading-time": "^1.5.0",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
@ -247,6 +249,9 @@
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"yargs": "^18.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"pm-astro": "dist/bin.js"
|
||||
}
|
||||
},
|
||||
"../polymech-mono/packages/cache": {
|
||||
|
||||
@ -4,11 +4,11 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"generate:config": "npx vite-node scripts/generate-app-config.ts",
|
||||
"generate:config": "pm-astro build:config",
|
||||
"dev": "npm run generate:config && astro dev --mode dev --host=0.0.0.0",
|
||||
"dev:all": "concurrently \"npm run dev\" \"npm run serve:products\"",
|
||||
"start": "astro dev",
|
||||
"build": "npm run generate:config && astro build -- --logLevel=info --branch=test",
|
||||
"build": "pm-astro build -- --logLevel=info --branch=site-dev",
|
||||
"test:build": "astro build ; cd dist ; serve",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
|
||||
@ -1,64 +0,0 @@
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { quicktype, InputData, jsonInputForTargetLanguage } from 'quicktype-core';
|
||||
|
||||
const CONFIG_PATH = path.resolve('./app-config.json');
|
||||
const OUTPUT_SCHEMA_PATH = path.resolve('./src/app/config.schema.ts');
|
||||
const OUTPUT_DTS_PATH = path.resolve('./src/app/config.d.ts');
|
||||
|
||||
async function main() {
|
||||
console.log(`Reading config from ${CONFIG_PATH}...`);
|
||||
const configContent = fs.readFileSync(CONFIG_PATH, 'utf8');
|
||||
|
||||
// 1. Generate TypeScript Definitions (d.ts) FIRST
|
||||
console.log('Generating TypeScript definitions...');
|
||||
|
||||
const tsInput = jsonInputForTargetLanguage("ts");
|
||||
await tsInput.addSource({
|
||||
name: "AppConfig",
|
||||
samples: [configContent]
|
||||
});
|
||||
|
||||
const tsInputData = new InputData();
|
||||
tsInputData.addInput(tsInput);
|
||||
|
||||
const tsResult = await quicktype({
|
||||
inputData: tsInputData,
|
||||
lang: "ts",
|
||||
rendererOptions: {
|
||||
"just-types": "true",
|
||||
"acronym-style": "original"
|
||||
}
|
||||
});
|
||||
|
||||
const tsCode = tsResult.lines.join('\n');
|
||||
fs.writeFileSync(OUTPUT_DTS_PATH, tsCode);
|
||||
console.log(`Wrote TypeScript definitions to ${OUTPUT_DTS_PATH}`);
|
||||
|
||||
// 2. Generate Zod Schema from Types using ts-to-zod
|
||||
console.log('Generating Zod schema from types...');
|
||||
|
||||
try {
|
||||
const { execSync } = await import('child_process');
|
||||
// ts-to-zod <input> <output>
|
||||
// Use relative paths to avoid Windows path concatenation issues with ts-to-zod
|
||||
const relDts = path.relative(process.cwd(), OUTPUT_DTS_PATH);
|
||||
const relSchema = path.relative(process.cwd(), OUTPUT_SCHEMA_PATH);
|
||||
|
||||
execSync(`npx ts-to-zod "${relDts}" "${relSchema}"`, { stdio: 'inherit', cwd: process.cwd() });
|
||||
|
||||
// Append export type AppConfig
|
||||
fs.appendFileSync(OUTPUT_SCHEMA_PATH, `\nexport type AppConfig = z.infer<typeof appConfigSchema>;\n`);
|
||||
|
||||
console.log(`Wrote Zod schema to ${OUTPUT_SCHEMA_PATH}`);
|
||||
} catch (error) {
|
||||
console.error('Failed to generate Zod schema:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('Error fetching/generating config:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
@ -1,9 +1,8 @@
|
||||
|
||||
import { loadConfig } from '../src/app/config-loader.js';
|
||||
import { I18N_SOURCE_LANGUAGE } from "../src/app/constants.js";
|
||||
|
||||
try {
|
||||
const config = loadConfig(I18N_SOURCE_LANGUAGE);
|
||||
const config = loadConfig();
|
||||
console.log('LOGGING_NAMESPACE:', config.core.logging_namespace);
|
||||
console.log('SHOW_GALLERY:', config.features.show_gallery);
|
||||
} catch (e) {
|
||||
|
||||
@ -1,108 +0,0 @@
|
||||
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
|
||||
import { substitute } from "@polymech/commons/variables";
|
||||
import { appConfigSchema } from "./config.schema.js";
|
||||
import type { AppConfig } from "./config.schema.js";
|
||||
|
||||
const I18N_SOURCE_LANGUAGE = 'en';
|
||||
|
||||
const LIBRARY_CONFIG_PATH = path.resolve("./app-config.json");
|
||||
const USER_CONFIG_DEFAULT_PATH = path.resolve("./app-config.local.json");
|
||||
|
||||
function deepMerge(target: any, source: any): any {
|
||||
if (typeof source !== 'object' || source === null) {
|
||||
return source; // Primitives or null overwrite
|
||||
}
|
||||
if (Array.isArray(source)) {
|
||||
return source; // Arrays overwrite
|
||||
}
|
||||
if (typeof target !== 'object' || target === null || Array.isArray(target)) {
|
||||
return source; // Target is not mergeable object, overwrite
|
||||
}
|
||||
|
||||
const result = { ...target };
|
||||
for (const key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
const val = source[key];
|
||||
if (typeof val === 'object' && val !== null && !Array.isArray(val)) {
|
||||
result[key] = deepMerge(result[key], val);
|
||||
} else {
|
||||
result[key] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function loadConfig(
|
||||
locale: string = I18N_SOURCE_LANGUAGE,
|
||||
libraryPath: string = LIBRARY_CONFIG_PATH
|
||||
): AppConfig {
|
||||
// 1. Load Library Config (Defaults)
|
||||
let rawLibraryContent: string;
|
||||
try {
|
||||
rawLibraryContent = fs.readFileSync(libraryPath, 'utf-8');
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to read library config file at ${libraryPath}: ${error}`);
|
||||
}
|
||||
|
||||
const variables = {
|
||||
LANG: locale
|
||||
};
|
||||
|
||||
const substitutedLibraryContent = substitute(false, rawLibraryContent, variables);
|
||||
let libraryConfig: any;
|
||||
try {
|
||||
libraryConfig = JSON.parse(substitutedLibraryContent);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse library config JSON: ${error}`);
|
||||
}
|
||||
|
||||
// 2. Parse CLI Arguments
|
||||
// We assume the caller might want to pass args, or we just grab process.argv
|
||||
// We cast to any because yargs returns a complex type
|
||||
const argv: any = yargs(hideBin(process.argv)).parseSync ? yargs(hideBin(process.argv)).parseSync() : (yargs(hideBin(process.argv)) as any).argv;
|
||||
|
||||
// 3. Determine User Config Path
|
||||
// Check for --config <path>
|
||||
const userConfigPath = argv.config ? path.resolve(argv.config) : USER_CONFIG_DEFAULT_PATH;
|
||||
|
||||
// 4. Load User Config (if exists)
|
||||
let userConfig: any = {};
|
||||
if (fs.existsSync(userConfigPath)) {
|
||||
try {
|
||||
const rawUserContent = fs.readFileSync(userConfigPath, 'utf-8');
|
||||
const substitutedUserContent = substitute(false, rawUserContent, variables);
|
||||
userConfig = JSON.parse(substitutedUserContent);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to load or parse user config at ${userConfigPath}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Merge: Library <- User <- CLI
|
||||
// Note: yargs parses --config as part of argv, but also other flags like --core.logging_namespace
|
||||
// We filter out specific known CLI-only flags if needed, but config schema validation will drop unknown keys anyway?
|
||||
// Actually zod 'strip' is default in safeParse? No, usually it passes through unless strict().
|
||||
// We should probably rely on valid keys overwriting.
|
||||
|
||||
// CLI args often come with standard keys like '$0', '_' which we might want to exclude if we blindly merge.
|
||||
// However, deepMerge will add them.
|
||||
// Ideally we would only merge keys that exist in the schema, but dynamic is fine for now.
|
||||
|
||||
let mergedConfig = deepMerge(libraryConfig, userConfig);
|
||||
mergedConfig = deepMerge(mergedConfig, argv);
|
||||
|
||||
// 6. Validate
|
||||
const result = appConfigSchema.safeParse(mergedConfig);
|
||||
|
||||
if (!result.success) {
|
||||
// Pretty print error if possible or just message
|
||||
throw new Error(`Config validation failed: ${result.error.message}`);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
}
|
||||
7
src/app/config.d.ts
vendored
7
src/app/config.d.ts
vendored
@ -76,9 +76,10 @@ export interface FooterLeft {
|
||||
}
|
||||
|
||||
export interface I18N {
|
||||
store: string;
|
||||
cache: boolean;
|
||||
asset_path: string;
|
||||
store: string;
|
||||
cache: boolean;
|
||||
source_language: string;
|
||||
asset_path: string;
|
||||
}
|
||||
|
||||
export interface Metadata {
|
||||
|
||||
@ -62,6 +62,7 @@ export const devSchema = z.object({
|
||||
export const i18NSchema = z.object({
|
||||
store: z.string(),
|
||||
cache: z.boolean(),
|
||||
source_language: z.string(),
|
||||
asset_path: z.string()
|
||||
});
|
||||
|
||||
|
||||
@ -3,11 +3,10 @@ import { IMAGE_PRESET, E_BROADBAND_SPEED } from "./network.js"
|
||||
import { resolve, template } from '@polymech/commons'
|
||||
import { sync as read } from '@polymech/fs/read'
|
||||
import { sanitizeUri } from 'micromark-util-sanitize-uri'
|
||||
import { loadConfig } from './config-loader.js'
|
||||
|
||||
export const I18N_SOURCE_LANGUAGE = 'en'
|
||||
// Load config
|
||||
const config = loadConfig(I18N_SOURCE_LANGUAGE)
|
||||
import { loadConfig } from '@polymech/astro-base/app/config-loader'
|
||||
|
||||
const config = loadConfig()
|
||||
|
||||
export const OSR_ROOT = () => path.resolve(resolve(config.core.osr_root))
|
||||
export const FILE_SERVER_DEV = config.dev.file_server
|
||||
|
||||
0
src/app/loaders/config-loader.ts
Normal file
0
src/app/loaders/config-loader.ts
Normal file
@ -1,41 +0,0 @@
|
||||
import { translate } from '../base/i18n.js'
|
||||
import { I18N_SOURCE_LANGUAGE } from 'config/config.js'
|
||||
import config from "./config.json"
|
||||
import pMap from 'p-map'
|
||||
|
||||
export const items = async (opts: { locale: string }) => {
|
||||
const _T = async (text: string) => await translate(text, I18N_SOURCE_LANGUAGE, opts.locale)
|
||||
return [
|
||||
{
|
||||
"href": `/${opts.locale}`,
|
||||
"title": _T("Home"),
|
||||
"ariaLabel": "Home",
|
||||
"class": "hover:text-orange-600"
|
||||
},
|
||||
{
|
||||
"href": `/resources/home`,
|
||||
"title": _T("Resources"),
|
||||
"ariaLabel": "Resources",
|
||||
"class": "hover:text-orange-600"
|
||||
}
|
||||
]
|
||||
}
|
||||
const isAbsoluteUrl = (url: string): boolean => /^[a-zA-Z]+:/.test(url);
|
||||
|
||||
const createFooterLinks = async (items: any[], locale: string) => {
|
||||
const _T = async (text: string) => await translate(text, I18N_SOURCE_LANGUAGE, locale);
|
||||
|
||||
return await pMap(items, async (item) => {
|
||||
const translatedText = await _T(item.text); // Single translation call
|
||||
|
||||
return {
|
||||
"href": isAbsoluteUrl(item.href) ? item.href : `/${locale}${item.href}`,
|
||||
"title": translatedText, // Use cached translation
|
||||
"ariaLabel": translatedText, // Use cached translation
|
||||
"class": "hover:text-orange-600"
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const footer_left = (locale: string) => createFooterLinks(config.footer_left, locale);
|
||||
export const footer_right = (locale: string) => createFooterLinks(config.footer_right, locale);
|
||||
@ -24,12 +24,9 @@ const imagesSchema = z.object({
|
||||
|
||||
type Images = z.infer<typeof imagesSchema>;
|
||||
|
||||
import { loadConfig } from './config-loader.js'
|
||||
import { I18N_SOURCE_LANGUAGE } from "./config.js"
|
||||
|
||||
// Load config
|
||||
const config = loadConfig(I18N_SOURCE_LANGUAGE)
|
||||
import { loadConfig } from '@polymech/astro-base/app/config-loader.js'
|
||||
|
||||
const config = loadConfig()
|
||||
export const IMAGE_PRESET: Images =
|
||||
{
|
||||
[E_BROADBAND_SPEED.SLOW]: config.optimization.presets.slow,
|
||||
|
||||
@ -4,11 +4,10 @@ import { ComponentConfigSchema } from '@polymech/commons/component'
|
||||
import { loader } from '@polymech/astro-base/model/component.js'
|
||||
import { loader as howtoLoader } from './model/howto/howto.js'
|
||||
|
||||
import { loadConfig } from './app/config-loader.js'
|
||||
import { I18N_SOURCE_LANGUAGE } from "./app/constants.js"
|
||||
import { loadConfig } from '@polymech/astro-base/app/config-loader.js'
|
||||
|
||||
const config = loadConfig()
|
||||
|
||||
// Load config
|
||||
const config = loadConfig(I18N_SOURCE_LANGUAGE)
|
||||
const LIBARY_BRANCH = config.retail.library_branch
|
||||
|
||||
const library = defineCollection({
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
---
|
||||
import "flowbite";
|
||||
|
||||
import Navigation from "@polymech/astro-base/components/global/Navigation.astro";
|
||||
import { createMarkdownComponent } from "@/base/index.js";
|
||||
import { translate } from "@polymech/astro-base/base/i18n.js";
|
||||
import Translate from "@polymech/astro-base/components/i18n.astro";
|
||||
|
||||
@ -1,287 +0,0 @@
|
||||
import * as path from 'path'
|
||||
import { findUp } from 'find-up'
|
||||
import { } from 'astro:content'
|
||||
|
||||
import { sync as read } from '@polymech/fs/read'
|
||||
import { sync as exists } from '@polymech/fs/exists'
|
||||
import { filesEx, forward_slash, resolveConfig } from '@polymech/commons'
|
||||
import { ICADNodeSchema, IComponentConfig } from '@polymech/commons/component'
|
||||
import { RenderedContent, DataEntry } from "astro:content"
|
||||
import type { Loader, LoaderContext } from 'astro/loaders'
|
||||
|
||||
import {
|
||||
CAD_MAIN_MATCH, PRODUCT_BRANCHES,
|
||||
CAD_EXTENSIONS, CAD_MODEL_EXT, PRODUCT_DIR, PRODUCT_GLOB,
|
||||
PRODUCT_ROOT, CAD_EXPORT_CONFIGURATIONS,
|
||||
CAD_DEFAULT_CONFIGURATION,
|
||||
CAD_URL,
|
||||
parseBoolean,
|
||||
DEFAULT_CONTACT,
|
||||
default_image
|
||||
} from 'config/config.js'
|
||||
|
||||
import { env } from '@/base/index.js'
|
||||
import { gallery } from '@/base/media.js';
|
||||
|
||||
import { get } from '@polymech/commons/component'
|
||||
import { PFilterValid } from '@polymech/commons/filter'
|
||||
|
||||
import { IAssemblyData } from '@polymech/cad'
|
||||
import { logger as log } from '@/base/index.js'
|
||||
import { translate } from "@/base/i18n.js"
|
||||
import { I18N_SOURCE_LANGUAGE } from "config/config.js"
|
||||
import { slugify } from "@/base/strings.js"
|
||||
|
||||
export const ITEM_TYPE = 'product'
|
||||
|
||||
export interface IComponentConfigEx extends IComponentConfig {
|
||||
content: string
|
||||
extra_resources?: string
|
||||
shared_resources?: string
|
||||
readme?: string
|
||||
rel: string
|
||||
}
|
||||
export interface IStoreItem extends DataEntry {
|
||||
data: IComponentConfigEx
|
||||
rendered?: RenderedContent
|
||||
}
|
||||
|
||||
const filterBranch = (items: { rel: string, config, path }[], branch: string) => {
|
||||
if (!PRODUCT_BRANCHES) {
|
||||
return items
|
||||
}
|
||||
const branchItems = PRODUCT_BRANCHES[branch]
|
||||
if (!branchItems) {
|
||||
return items
|
||||
}
|
||||
return items.filter((item) => branchItems.includes(item.rel))
|
||||
}
|
||||
|
||||
export const items = (branch: string) =>{
|
||||
return []
|
||||
filterBranch(get(`${PRODUCT_ROOT()}/${PRODUCT_GLOB}`,
|
||||
PRODUCT_ROOT(), PFilterValid.marketplace_component), branch)
|
||||
}
|
||||
|
||||
const onComponent = async (item: IStoreItem, ctx: LoaderContext) => {
|
||||
/*
|
||||
const onNode = async (data: INodeCallback, configuration: string) => {
|
||||
if (!CAD_EXPORT_SUB_COMPONENTS || !data.target.endsWith('.json')) {
|
||||
return
|
||||
}
|
||||
const modelPath = `${CAD_MODEL_FILE_PATH(data.target,configuration)}`
|
||||
const model: IAssemblyData = read(modelPath, 'json') as IAssemblyData
|
||||
if (!model) {
|
||||
return
|
||||
}
|
||||
const configurations = Object.keys(model.Configurations).filter((c) => {
|
||||
return c !== CAD_DEFAULT_CONFIGURATION &&
|
||||
c !== 'Global' &&
|
||||
model.Configurations[c].Hide !== '1'
|
||||
})
|
||||
if (!configurations.length ||
|
||||
model.Configurations?.Global?.['Configurations'] !== '1') {
|
||||
return
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
export const defaults = async (data: IComponentConfigEx, cwd: string, root:string): Promise<IComponentConfigEx> => {
|
||||
let defaultsJSON = await findUp('defaults.json', {
|
||||
stopAt: root,
|
||||
cwd: cwd
|
||||
});
|
||||
try {
|
||||
if (defaultsJSON) {
|
||||
data = {
|
||||
...read(defaultsJSON, 'json') as any,
|
||||
...data,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const cad = async (item: IStoreItem): Promise<ICADNodeSchema[]> => {
|
||||
const root = PRODUCT_ROOT()
|
||||
const data: IComponentConfigEx = item.data
|
||||
const itemRel = data.rel
|
||||
const default_profile = env(itemRel)
|
||||
const mainAssemblies = filesEx(root, CAD_MAIN_MATCH(itemRel), { absolute: false })
|
||||
//log.debug(`Loading CAD/CAM for ${itemRel} in ${root} with ${CAD_MAIN_MATCH(itemRel)}`)
|
||||
const cadMeta = mainAssemblies.map((file: string): ICADNodeSchema => {
|
||||
const result: ICADNodeSchema = {
|
||||
file,
|
||||
name: path.basename(file)
|
||||
}
|
||||
CAD_EXTENSIONS.forEach(ext => result[ext] = decodeURIComponent(forward_slash(CAD_URL(file.replace('.SLDASM', ext), data as Record<string, string>))))
|
||||
result.model = path.join(root, file.replace('.SLDASM', CAD_MODEL_EXT))
|
||||
return result
|
||||
})
|
||||
if (!cadMeta.length) {
|
||||
return []
|
||||
}
|
||||
item.data.cad = cadMeta
|
||||
item.data.preview3d = cadMeta[0].html
|
||||
|
||||
if (CAD_EXPORT_CONFIGURATIONS) {
|
||||
const assemblies = cadMeta.filter((assembly) => assembly.model && exists(assembly.model))
|
||||
const components = cadMeta.map((assembly) => {
|
||||
const modelPath = assembly.model as string
|
||||
const model: IAssemblyData = read(modelPath, 'json') as IAssemblyData
|
||||
if (!model) {
|
||||
return
|
||||
}
|
||||
const configurations = Object.keys(model.Configurations).filter((c) => {
|
||||
return c !== CAD_DEFAULT_CONFIGURATION &&
|
||||
parseBoolean(model.Configurations[c].Hide || '')
|
||||
})
|
||||
if (!configurations.length ||
|
||||
parseBoolean(model.Configurations?.Global?.Configurations || '')) {
|
||||
return
|
||||
}
|
||||
log.debug(`Loading CAD/CAM for ${itemRel}`, configurations)
|
||||
})
|
||||
}
|
||||
return cadMeta
|
||||
}
|
||||
|
||||
const onItem = async (item: IStoreItem, ctx: LoaderContext) => {
|
||||
if (!item || !item.data) {
|
||||
ctx.logger.error(`Error completing ${''}: no data`);
|
||||
return
|
||||
}
|
||||
const { logger } = ctx
|
||||
let data: IComponentConfigEx = item.data
|
||||
const itemRel = data.rel
|
||||
const itemRelMin = itemRel.replace('products/', '')
|
||||
const itemDir = PRODUCT_DIR(itemRel)
|
||||
const default_profile = env(itemRel)
|
||||
data.product_rel = itemRelMin
|
||||
data.assets = {
|
||||
renderings: [],
|
||||
gallery: []
|
||||
}
|
||||
data.body = (read(path.join(itemDir, 'templates/shared', 'body.md')) as string) || ""
|
||||
data.resources = (read(path.join(itemDir, 'templates/shared', 'resources.md')) as string) || ""
|
||||
//////////////////////////////////////////
|
||||
//
|
||||
// Item Shared Resources
|
||||
//
|
||||
let resourcesDefaultPath = await findUp('resources.md', {
|
||||
stopAt: PRODUCT_ROOT(),
|
||||
cwd: itemDir
|
||||
}) || ""
|
||||
data.shared = (read(resourcesDefaultPath) as string) || ""
|
||||
//////////////////////////////////////////
|
||||
//
|
||||
// Readme
|
||||
//
|
||||
let readmePath = path.join(itemDir, 'Readme.md')
|
||||
data.readme = (read(readmePath) as string) || ""
|
||||
//////////////////////////////////////////
|
||||
//
|
||||
// Variables
|
||||
//
|
||||
data = await defaults(data, itemDir, PRODUCT_ROOT());
|
||||
data = {
|
||||
...data,
|
||||
...default_profile.variables,
|
||||
product_rel_min: itemRelMin,
|
||||
}
|
||||
data = resolveConfig(data as Record<string, string>) as IComponentConfigEx
|
||||
item.data = data
|
||||
//////////////////////////////////////////
|
||||
//
|
||||
// Extensions, CAD, Media, etcetera etcetera :)
|
||||
//
|
||||
data.cad = await cad(item)
|
||||
|
||||
data.assets.renderings = await gallery('renderings', data.rel)
|
||||
data.assets.gallery = await gallery('media/gallery', data.rel)
|
||||
data.image = data.assets.renderings[0] || default_image()
|
||||
data.assets.showcase = await gallery('media/showcase', data.rel)
|
||||
data.assets.samples = await gallery('media/samples', data.rel)
|
||||
data.checkout = await item_checkout(data)
|
||||
}
|
||||
|
||||
export function loader(branch: string): Loader {
|
||||
const load = async ({
|
||||
config,
|
||||
logger,
|
||||
watcher,
|
||||
parseData,
|
||||
store,
|
||||
generateDigest }: LoaderContext) => {
|
||||
|
||||
store.clear();
|
||||
let products = items(branch)
|
||||
for (const item of products) {
|
||||
const product: any = item.config
|
||||
const id = product.slug;
|
||||
const data = {
|
||||
rel: item.rel,
|
||||
slug: id,
|
||||
id,
|
||||
title: product.name,
|
||||
type: ITEM_TYPE,
|
||||
highlights: [],
|
||||
components: [],
|
||||
...product,
|
||||
}
|
||||
//const parsedData = await parseData({ id, data: data });
|
||||
const storeItem = {
|
||||
digest: await generateDigest(data),
|
||||
filePath: id,
|
||||
assetImports: [],
|
||||
id: `${item.rel}`,
|
||||
data: data
|
||||
}
|
||||
await onItem(storeItem, {
|
||||
logger,
|
||||
watcher,
|
||||
parseData,
|
||||
store,
|
||||
generateDigest
|
||||
} as any)
|
||||
|
||||
storeItem.data['config'] = JSON.stringify(storeItem.data, null, 2)
|
||||
store.set(storeItem)
|
||||
}
|
||||
}
|
||||
return {
|
||||
name: `astro:store:${ITEM_TYPE}`,
|
||||
load
|
||||
};
|
||||
}
|
||||
|
||||
export const group_path = (item) => item.id.split("/")[1]
|
||||
|
||||
const group_label = async (text: string, locale) => await translate(slugify(text), I18N_SOURCE_LANGUAGE, locale)
|
||||
|
||||
const group = async (items, locale) => {
|
||||
return items.reduce(async (accPromise, item) => {
|
||||
const acc = await accPromise
|
||||
const id = group_path(item)
|
||||
let key: string = await group_label(id, locale)
|
||||
key = key.charAt(0).toUpperCase() + key.slice(1)
|
||||
if (!acc[key]) {
|
||||
acc[key] = []
|
||||
}
|
||||
acc[key].push(item)
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
export const group_by_path = async (items, locale): Promise<IComponentConfigEx[]> => await group(items, locale)
|
||||
|
||||
export const mailto = (to: string, subject: string, body: string): string => {
|
||||
const encode = (str: string) => encodeURIComponent(str).replace(/%20/g, '+');
|
||||
return `mailto:${encode(to)}?subject=${encode(subject)}&body=${encode(body)}`;
|
||||
}
|
||||
|
||||
export const item_checkout = async (item: IComponentConfig) => {
|
||||
return `mailto:${DEFAULT_CONTACT}?subject=${item.name}&body=${""}`
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user