UNPKG

@knapsack/app

Version:

Build Design Systems with Knapsack

343 lines (341 loc) • 14.5 kB
#! /usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /** * Copyright (C) 2018 Basalt This file is part of Knapsack. Knapsack is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Knapsack is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Knapsack; if not, see <https://www.gnu.org/licenses>. */ const creator_utils_1 = require("@knapsack/creator-utils"); const path_1 = require("path"); const file_utils_1 = require("@knapsack/file-utils"); const utils_1 = require("@knapsack/utils"); const log_1 = require("./log"); const events_1 = require("../server/events"); const server_1 = require("../server/server"); const commands_1 = require("./commands"); const bootstrap_1 = require("../lib/bootstrap"); const cache_dir_1 = require("../lib/util/cache-dir"); const static_paths_1 = require("../lib/static-paths"); const constants_1 = require("../lib/constants"); process.on('uncaughtException', (error) => { log_1.log.error(error, { errorType: 'uncaughtException', }); }); /** * event is emitted whenever a Promise is rejected and no error handler is attached to the promise within a turn of the event loop * https://nodejs.org/api/process.html#event-unhandledrejection */ process.on('unhandledRejection', (reason, _promise) => { if (reason instanceof Error) { log_1.log.error(reason, { errorType: 'unhandledRejection', }); } else { log_1.log.error(`Unhandled Rejection: ${reason}`, { errorType: 'unhandledRejection', }); } }); (0, log_1.setupUpdateNotifier)({ name: '@knapsack/app', version: constants_1.APP_CLIENT_VERSION, }); const ksBrain = (0, bootstrap_1.bootstrapForCli)(); const { patterns, config, assetSets } = ksBrain; (0, creator_utils_1.createCreatorCli)({ creators: [ ...config.templateRenderers.flatMap((renderer) => { var _a; return (_a = renderer.creators) !== null && _a !== void 0 ? _a : []; }), ], disableCreatorsAsDefaultCommand: true, config: { dest: config.data, }, alterYargs: async (yargs) => { yargs.scriptName('knapsack'); yargs.option('config', { type: 'string', description: 'Path to knapsack.config.cjs file', }); yargs.option('verbose', { type: 'boolean', description: 'Show verbose output', coerce(arg) { (0, log_1.setLogVerbose)(arg); return arg; }, }); yargs.command('serve', '', {}, async () => { try { (0, log_1.updateLogMetadata)({ env: 'production', metadata: { cliCommand: 'serve', }, }); if (!cache_dir_1.appClientDataFileHelper.exists()) { throw new Error(`Please run "knapsack build" before trying to run "knapsack serve"`); } const { appClientDataNoMeta, discovery } = await (0, commands_1.hydrateAll)({ ksBrain, }); const { serve } = await (0, server_1.getServer)({ ksBrain, getDataStore: async () => appClientDataNoMeta, command: 'serve', discovery, }); return serve(); } catch (e) { log_1.log.error(e); process.exit(1); } }); yargs.command('build', '', {}, async function cliBuild() { try { (0, log_1.updateLogMetadata)({ env: 'production', metadata: { cliCommand: 'build', }, }); await (0, file_utils_1.emptyDir)(config.dist); const { discovery } = await (0, commands_1.initAll)({ ksBrain, missingFileVerbosity: 'warn', }); await Promise.all([ (0, commands_1.getDataStore)(ksBrain).then(cache_dir_1.appClientDataFileHelper.write), cache_dir_1.discoveryFileHelper.write(discovery), ...(0, static_paths_1.getStaticPaths)({ config, assetSetsPublicPaths: ksBrain.assetSets.publicPaths, }).map(async ({ dirPath, publicPathBase }) => { const dest = (0, path_1.join)(cache_dir_1.ksCacheDir, // all of these start with `/` so the initial `.` turns it into `./` `.${publicPathBase}`); log_1.log.verbose(`Copying ${dirPath} to ${dest}`); // dereference: true is needed to copy the actual files instead of the // symlinks await (0, file_utils_1.copy)(dirPath, dest, { dereference: true }); }), ]); await (0, commands_1.build)({ config, ksBrain }); const { demos } = await ksBrain.db.getData(); await (0, commands_1.writeTemplateMeta)({ allPatterns: patterns.allPatterns, templateRenderers: config.templateRenderers, distDir: config.dist, allAssetSetIds: assetSets.getGlobalAssetSets().map((a) => a.id), demosById: demos.byId, }); if (config.plugins) { await Promise.all(config.plugins .filter((p) => p.onBuild) .map((p) => p.onBuild({ brain: ksBrain }))); } // Immediately write out all files in "./data" dir to minimize unnecessary // git diffs during PRs from Propose Changes (since this function uses the // same `savePrepNewDataStore` that both the Propose Change AND saving // locally do) await (0, commands_1.ensureDataDirFilesFormatted)({ ksBrain, }); const used = process.memoryUsage().heapUsed / 1024 / 1024; log_1.log.info(`The script uses approximately ${Math.round(used * 100) / 100} MB`); events_1.knapsackEvents.emitShutdown(); process.exit(0); } catch (err) { log_1.log.error(err); process.exit(1); } }); yargs.command('build:tokens', 'Write out design tokens files in various sources (like scss, js, etc)', {}, async () => { (0, log_1.updateLogMetadata)({ env: 'production', metadata: { cliCommand: 'build:tokens', }, }); try { await ksBrain.tokens.build(); log_1.log.info(`Wrote design tokens to "${(0, path_1.relative)(process.cwd(), ksBrain.tokens.distDir)}"`); events_1.knapsackEvents.emitShutdown(); process.exit(0); } catch (e) { log_1.log.error(e); process.exit(1); } }); yargs.command('build:meta', 'Write out metadata files (i.e. Pattern Spec (props/slots) info as TypeScript types & JSON Schemas)', {}, async () => { (0, log_1.updateLogMetadata)({ env: 'production', metadata: { cliCommand: 'build:meta', }, }); try { await (0, commands_1.initAll)({ ksBrain, missingFileVerbosity: 'warn', }); const { demos } = await ksBrain.db.getData(); const { metaDir } = await (0, commands_1.writeTemplateMeta)({ allPatterns: patterns.allPatterns, templateRenderers: config.templateRenderers, distDir: config.dist, allAssetSetIds: assetSets.getGlobalAssetSets().map((a) => a.id), demosById: demos.byId, }).catch((err) => { log_1.log.error('Knapsack writeTemplateMeta error', err, 'build'); process.exit(1); }); log_1.log.info(`Wrote metadata to "${(0, path_1.relative)(process.cwd(), metaDir)}"`); events_1.knapsackEvents.emitShutdown(); process.exit(0); } catch (e) { log_1.log.error(e); process.exit(1); } }); yargs.command('start', 'Run for local development', {}, async () => { try { (0, log_1.updateLogMetadata)({ env: 'development', metadata: { cliCommand: 'start', }, }); await (0, file_utils_1.emptyDir)(config.dist); const { discovery } = await (0, commands_1.initAll)({ ksBrain, isStartCmd: true, missingFileVerbosity: 'warn', }); const { data: httpsCert, error: httpsCertError } = await (0, utils_1.tryCatch)((0, commands_1.getHttpsCert)(ksBrain.config.devServer)); if (httpsCertError) { throw httpsCertError; } Object.values(ksBrain).forEach((item) => 'watch' in item ? item.watch() : undefined); // Immediately write out all files in "./data" dir to minimize unnecessary // git diffs during PRs from Propose Changes (since this function uses the // same `savePrepNewDataStore` that both the Propose Change AND saving // locally do) await (0, commands_1.ensureDataDirFilesFormatted)({ ksBrain, }); const getDataStore2 = () => (0, commands_1.getDataStore)(ksBrain); const templateRendererWatches = config.templateRenderers.filter((t) => t.watch); const { serve } = await (0, server_1.getServer)({ ksBrain, getDataStore: getDataStore2, command: 'start', discovery, httpsCert, }); const { demos } = await ksBrain.db.getData(); const writeMeta = async () => { await (0, commands_1.writeTemplateMeta)({ templateRenderers: config.templateRenderers, allPatterns: patterns.allPatterns, distDir: config.dist, allAssetSetIds: assetSets.getGlobalAssetSets().map((a) => a.id), demosById: demos.byId, }); }; events_1.knapsackEvents.onPatternsDataReady(async () => { try { await writeMeta(); } catch (writeTemplateMetaError) { log_1.log.warn(writeTemplateMetaError); } }); return Promise.all([ writeMeta(), ...templateRendererWatches.map((t) => t.watch()), serve(), ]); } catch (err) { log_1.log.error(err); process.exit(1); } }); yargs.command('test', 'Test all Patterns for successful renders', {}, async () => { try { (0, log_1.updateLogMetadata)({ env: 'development', metadata: { cliCommand: 'test', }, }); await (0, commands_1.initAll)({ ksBrain, missingFileVerbosity: 'error', }); await (0, commands_1.build)({ ksBrain, config, skipDiscovery: true }).catch((err) => { log_1.log.error(err); process.exit(1); }); await (0, commands_1.testPatternRenders)(patterns.allPatterns, ksBrain).catch((e) => { events_1.knapsackEvents.emitShutdown(); process.exit(1); }); events_1.knapsackEvents.emitShutdown(); process.exit(0); } catch (e) { log_1.log.error(e); process.exit(1); } }); yargs.command('clear-cache', 'Clears the Knapsack cache', {}, async () => { try { await (0, cache_dir_1.emptyKsCacheDir)(); log_1.log.info('Cleared cache'); process.exit(0); } catch (e) { log_1.log.error(e); process.exit(1); } }); yargs.command('data', 'Outputs the whole App Client Data as JSON', {}, async () => { try { if (!cache_dir_1.appClientDataFileHelper.exists()) { log_1.log.error(`Please run "knapsack build" before trying to run this command`); process.exit(1); } const data = await cache_dir_1.appClientDataFileHelper.read(); process.stdout.write(JSON.stringify(data, null, ' ')); } catch (e) { log_1.log.error(e); process.exit(1); } }); // yargs.command('upgrade-config', '', {}, async () => { // knapsackEvents.emitShutdown(); // }); }, }); //# sourceMappingURL=knapsack-cli.js.map