@knapsack/app
Version:
Build Design Systems with Knapsack
343 lines (341 loc) • 14.5 kB
JavaScript
;
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