UNPKG

6.83 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.generate = void 0;
4const crypto_1 = require("crypto");
5const path_1 = require("path");
6const codegen_js_1 = require("./codegen.js");
7const config_js_1 = require("./config.js");
8const hooks_js_1 = require("./hooks.js");
9const debugging_js_1 = require("./utils/debugging.js");
10const file_system_js_1 = require("./utils/file-system.js");
11const watcher_js_1 = require("./utils/watcher.js");
12const hash = (content) => (0, crypto_1.createHash)('sha1').update(content).digest('base64');
13async function generate(input, saveToFile = true) {
14 const context = (0, config_js_1.ensureContext)(input);
15 const config = context.getConfig();
16 await context.profiler.run(() => (0, hooks_js_1.lifecycleHooks)(config.hooks).afterStart(), 'Lifecycle: afterStart');
17 let previouslyGeneratedFilenames = [];
18 function removeStaleFiles(config, generationResult) {
19 const filenames = generationResult.map(o => o.filename);
20 // find stale files from previous build which are not present in current build
21 const staleFilenames = previouslyGeneratedFilenames.filter(f => !filenames.includes(f));
22 staleFilenames.forEach(filename => {
23 if (shouldOverwrite(config, filename)) {
24 return (0, file_system_js_1.unlinkFile)(filename, err => {
25 const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
26 if (err) {
27 (0, debugging_js_1.debugLog)(`Cannot remove stale file: ${prettyFilename}\n${err}`);
28 }
29 else {
30 (0, debugging_js_1.debugLog)(`Removed stale file: ${prettyFilename}`);
31 }
32 });
33 }
34 });
35 previouslyGeneratedFilenames = filenames;
36 }
37 const recentOutputHash = new Map();
38 async function writeOutput(generationResult) {
39 if (!saveToFile) {
40 return generationResult;
41 }
42 if (config.watch) {
43 removeStaleFiles(config, generationResult);
44 }
45 await context.profiler.run(async () => {
46 await (0, hooks_js_1.lifecycleHooks)(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
47 }, 'Lifecycle: beforeAllFileWrite');
48 await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
49 const previousHash = recentOutputHash.get(result.filename) || (await hashFile(result.filename));
50 const exists = previousHash !== null;
51 // Store previous hash to avoid reading from disk again
52 if (previousHash) {
53 recentOutputHash.set(result.filename, previousHash);
54 }
55 if (!shouldOverwrite(config, result.filename) && exists) {
56 return;
57 }
58 let content = result.content || '';
59 const currentHash = hash(content);
60 if (previousHash && currentHash === previousHash) {
61 (0, debugging_js_1.debugLog)(`Skipping file (${result.filename}) writing due to indentical hash...`);
62 return;
63 }
64 // skip updating file in dry mode
65 if (context.checkMode) {
66 context.checkModeStaleFiles.push(result.filename);
67 return;
68 }
69 if (content.length === 0) {
70 return;
71 }
72 const absolutePath = (0, path_1.isAbsolute)(result.filename)
73 ? result.filename
74 : (0, path_1.join)(input.cwd || process.cwd(), result.filename);
75 const basedir = (0, path_1.dirname)(absolutePath);
76 await (0, file_system_js_1.mkdirp)(basedir);
77 content = await (0, hooks_js_1.lifecycleHooks)(result.hooks).beforeOneFileWrite(absolutePath, content);
78 content = await (0, hooks_js_1.lifecycleHooks)(config.hooks).beforeOneFileWrite(absolutePath, content);
79 if (content !== result.content) {
80 result.content = content;
81 // compare the prettified content with the previous hash
82 // to compare the content with an existing prettified file
83 if (hash(content) === previousHash) {
84 (0, debugging_js_1.debugLog)(`Skipping file (${result.filename}) writing due to indentical hash after prettier...`);
85 // the modified content is NOT stored in recentOutputHash
86 // so a diff can already be detected before executing the hook
87 return;
88 }
89 }
90 await (0, file_system_js_1.writeFile)(absolutePath, result.content);
91 recentOutputHash.set(result.filename, currentHash);
92 await (0, hooks_js_1.lifecycleHooks)(result.hooks).afterOneFileWrite(result.filename);
93 await (0, hooks_js_1.lifecycleHooks)(config.hooks).afterOneFileWrite(result.filename);
94 })), 'Write files');
95 await context.profiler.run(() => (0, hooks_js_1.lifecycleHooks)(config.hooks).afterAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: afterAllFileWrite');
96 return generationResult;
97 }
98 // watch mode
99 if (config.watch) {
100 return (0, watcher_js_1.createWatcher)(context, writeOutput);
101 }
102 const outputFiles = await context.profiler.run(() => (0, codegen_js_1.executeCodegen)(context), 'executeCodegen');
103 await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
104 await context.profiler.run(() => (0, hooks_js_1.lifecycleHooks)(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
105 if (context.profilerOutput) {
106 await (0, file_system_js_1.writeFile)((0, path_1.join)(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
107 }
108 return outputFiles;
109}
110exports.generate = generate;
111function shouldOverwrite(config, outputPath) {
112 const globalValue = config.overwrite === undefined ? true : !!config.overwrite;
113 const outputConfig = config.generates[outputPath];
114 if (!outputConfig) {
115 (0, debugging_js_1.debugLog)(`Couldn't find a config of ${outputPath}`);
116 return globalValue;
117 }
118 if (isConfiguredOutput(outputConfig) && typeof outputConfig.overwrite === 'boolean') {
119 return outputConfig.overwrite;
120 }
121 return globalValue;
122}
123function isConfiguredOutput(output) {
124 return typeof output.plugins !== 'undefined';
125}
126async function hashFile(filePath) {
127 try {
128 return hash(await (0, file_system_js_1.readFile)(filePath));
129 }
130 catch (err) {
131 if (err && err.code === 'ENOENT') {
132 // return null if file does not exist
133 return null;
134 }
135 // rethrow unexpected errors
136 throw err;
137 }
138}