@adpt/cli
Version:
AdaptJS command line interface
174 lines • 6.63 kB
JavaScript
;
/*
* Copyright 2018-2019 Unbounded Systems, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const fs = tslib_1.__importStar(require("fs-extra"));
const pacote = tslib_1.__importStar(require("pacote"));
const path = tslib_1.__importStar(require("path"));
const utils_1 = require("@adpt/utils");
const error_1 = require("../error");
const adapt_shared_1 = require("../types/adapt_shared");
const utils_2 = require("../utils");
const defaultOptions = {
loglevel: "normal",
progress: true,
};
async function finalProjectOptions(userOpts) {
let session = userOpts && userOpts.session;
if (!session)
session = await tempSession();
session.projectDir = path.resolve(session.projectDir);
return Object.assign({}, defaultOptions, userOpts, { session });
}
function yarnCommonOptions(projOpts, tmpModules) {
const yOpts = {
cwd: projOpts.session.projectDir,
loglevel: projOpts.loglevel,
noProgress: !projOpts.progress,
};
if (projOpts.registry)
yOpts.registry = projOpts.registry;
if (tmpModules)
yOpts.modulesFolder = tmpModules;
return yOpts;
}
function yarnInstallOptions(projOpts, tmpModules) {
const yOpts = yarnCommonOptions(projOpts, tmpModules);
return Object.assign({}, yOpts, { production: true });
}
async function load(projectSpec, projectOpts) {
const finalOpts = await finalProjectOptions(projectOpts);
const session = finalOpts.session;
const pacoteOpts = {
cache: session.cacheDir,
};
if (finalOpts.registry)
pacoteOpts.registry = finalOpts.registry;
const manifest = await pacote.manifest(projectSpec, pacoteOpts);
let inPlace = false;
if (utils_2.isLocal(projectSpec)) {
projectSpec = path.resolve(projectSpec);
if (projectSpec === session.projectDir)
inPlace = true;
}
if (!inPlace)
await pacote.extract(projectSpec, session.projectDir, pacoteOpts);
return new Project(manifest, session.projectDir, finalOpts);
}
exports.load = load;
async function projectAdaptModule(projectRoot) {
const entryFile = require.resolve("@adpt/core", { paths: [projectRoot] });
// Load Adapt at runtime. We've already done some version
// verification before getting here, but we don't
// have types.
// TODO(mark): What's the right way to type this? We actually
// don't want to have a dependency on @adpt/core.
// tslint:disable-next-line:no-implicit-dependencies
return adapt_shared_1.verifyAdaptModule(await require(entryFile));
}
exports.projectAdaptModule = projectAdaptModule;
class Project {
constructor(manifest, projectDir, options) {
this.manifest = manifest;
this.projectDir = projectDir;
this.options = options;
this.installed = false;
this.name = manifest.name;
}
getLockedVersion(pkgName) {
if (!this.installed) {
throw new Error(`Internal error: must call installModules before checking package versions`);
}
try {
const pkgJsonPath = path.join(this.projectDir, "node_modules", pkgName, "package.json");
const pkgJson = fs.readJsonSync(pkgJsonPath);
const version = pkgJson.version;
if (!version || typeof version !== "string") {
throw new Error(`Version information for package ${pkgName} is invalid (${version})`);
}
return version;
}
catch (err) {
return null;
}
}
async create(options) {
return this.deploy(options, (adapt) => adapt.createDeployment(options));
}
async update(options) {
return this.deploy(options, (adapt) => adapt.updateDeployment(options));
}
async status(options) {
return this.deploy(options, (adapt) => adapt.fetchStatus(options));
}
/**
* NOTE: This function is purposely NOT async and returns the promise-like
* execa ChildProcess object, NOT a promise to that object. That gives
* the caller access to the output streams without having to wait for
* completion of the yarn process.
*/
installModules() {
if (this.installed)
return;
const ret = utils_1.yarn.install(yarnInstallOptions(this.options));
ret.then(() => this.installed = true).catch();
return ret;
}
async deploy(options, action) {
if (!this.installed) {
throw new Error(`Internal error: must call installModules before any deploy operation`);
}
const projectRoot = this.options.session.projectDir;
options.fileName = path.resolve(projectRoot, options.fileName);
options.projectRoot = projectRoot;
const adapt = await projectAdaptModule(projectRoot);
try {
return adapt_shared_1.verifyDeployState(await action(adapt));
}
catch (err) {
if (err instanceof adapt.ProjectCompileError) {
if (err.message)
throw new error_1.UserError(err.message);
}
else if (err instanceof adapt.ProjectRunError) {
if (err.message && err.projectStack) {
throw new error_1.UserError(`${err.message}\n${err.projectStack}`);
}
}
else if (err instanceof utils_1.ValidationError) {
throw new Error(`Internal error: unrecognized response ` +
`from Adapt build: ${err.message}`);
}
throw err;
}
}
}
exports.Project = Project;
async function tempSession() {
const dir = await utils_1.mkdtmp("adapt-cli");
const cacheDir = path.join(dir, "cache");
const projectDir = path.join(dir, "project");
await fs.ensureDir(cacheDir);
await fs.ensureDir(projectDir);
return {
cacheDir,
projectDir,
remove: () => fs.removeSync(dir),
};
}
exports.tempSession = tempSession;
//# sourceMappingURL=project.js.map