UNPKG

@stencila/jesta

Version:

Stencila plugin for executable documents using JavaScript

110 lines (109 loc) 4.76 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.build = void 0; const schema_1 = require("@stencila/schema"); const child_process_1 = require("child_process"); const fs_1 = __importDefault(require("fs")); const util_1 = require("util"); const types_1 = require("./types"); const changes_1 = require("./util/changes"); const timer = __importStar(require("./util/timer")); const walk_1 = require("./util/walk"); const readFile = util_1.promisify(fs_1.default.readFile); const writeFile = util_1.promisify(fs_1.default.writeFile); /** * Build a document * * This implementation walks the document and collects NPM package names * from the `imports` property of Javascript `CodeChunk` nodes. It then * calls `npm install` with the names of the packages missing from `package.json`. * If there is no `package.json` file then will create one. * * @param entity The document to build. */ async function build(node, force) { // Build code chunks (code expressions can be ignored) if (schema_1.isA('CodeChunk', node)) { // Skip if not not JavaScript code const { programmingLanguage } = node; if (!['js', 'javascript'].includes(programmingLanguage !== null && programmingLanguage !== void 0 ? programmingLanguage : '')) return node; // Skip if not needed if (!force && !changes_1.needed(node, types_1.Method.build)) return node; // Ensure node has been compiled await this.compile(node, force); // Start the timer const start = timer.start(); // Collect a list of packages imported within the node const { imports = [] } = node; const packages = []; for (const pkg of imports) { const name = typeof pkg === 'string' ? pkg : pkg.name; if (name !== undefined && !packages.includes(name)) packages.push(name); } // Read or create the document's package.json if (packages.length > 0) { let packageJson; try { const json = await readFile('package.json', 'utf8'); packageJson = JSON.parse(json); } catch (error) { if (error.code === 'ENOENT') { // Write out document's package.json with the `dependencies` property // updated and defaults for other properties (mainly to prevent `npm` // warning when they are absent) packageJson = { description: 'NPM package for this document', repository: '-', license: 'Apache-2.0', dependencies: {}, }; await writeFile('package.json', JSON.stringify(packageJson, null, ' '), 'utf8'); } else throw error; } // Only install packages that are not yet in package.json const missing = packages.filter((pkg) => packageJson.dependencies[pkg] === undefined); if (missing.length > 0) { // Ask `npm` to install the packages child_process_1.spawnSync('npm', ['install', '--save-exact', ...missing], { // TODO: Transform output from stdout and stderr into log entries stdio: ['ignore', 'inherit', 'inherit'], }); } } // Record the method return changes_1.record(node, types_1.Method.build, timer.seconds(start)); } else { // Walk over other node types return walk_1.mutate(node, (child) => this.build(child, force)); } } exports.build = build;