UNPKG

15.8 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3/*
4 * Copyright 2014-2018 Guy Bedford (http://guybedford.com)
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18const path = require("path");
19const rimraf = require("rimraf");
20const events = require("events");
21const mkdirp = require("mkdirp");
22const common_1 = require("./utils/common");
23const ui_1 = require("./utils/ui");
24const config_1 = require("./config");
25const registry_manager_1 = require("./install/registry-manager");
26const cache_1 = require("./utils/cache");
27const global_config_file_1 = require("./config/global-config-file");
28const fetch_1 = require("./install/fetch");
29const cjs_convert_1 = require("./compile/cjs-convert");
30// import { ExactPackage, PackageName, clearPackageCache } from './utils/package';
31const install_1 = require("./install");
32const run_cmd_1 = require("./utils/run-cmd");
33const api_1 = require("./api");
34// git is required for jspm to work
35let hasGit = true;
36try {
37 require('which').sync('git');
38}
39catch (e) {
40 hasGit = false;
41}
42function applyDefaultConfiguration(userConfig) {
43 const config = Object.assign({}, userConfig);
44 if (!config.registries) {
45 const registriesGlobalConfig = global_config_file_1.default.get('registries') || {};
46 const registries = {};
47 Object.keys(registriesGlobalConfig).forEach((registryName) => {
48 const registry = registriesGlobalConfig[registryName];
49 if (registry.handler === 'jspm-npm' || registry.handler === 'jspm-github')
50 registry.handler = undefined;
51 registries[registryName] = {
52 handler: registry.handler || `@jspm/${registryName}`,
53 config: registry
54 };
55 });
56 config.registries = registries;
57 }
58 if (!config.defaultRegistry) {
59 let defaultRegistry = global_config_file_1.default.get('defaultRegistry');
60 if (!defaultRegistry || defaultRegistry === 'jspm')
61 defaultRegistry = 'npm';
62 config.defaultRegistry = defaultRegistry;
63 }
64 if ('offline' in config === false)
65 config.offline = false;
66 if ('preferOffline' in config === false)
67 config.preferOffline = global_config_file_1.default.get('preferOffline') || false;
68 if ('cli' in config === false)
69 config.cli = true;
70 if ('timeouts' in config === false)
71 config.timeouts = {
72 resolve: global_config_file_1.default.get('timeouts.resolve') || 30000,
73 download: global_config_file_1.default.get('timeouts.download') || 300000
74 };
75 if ('userInput' in config === false)
76 config.userInput = true;
77 if ('cacheDir' in config === false)
78 config.cacheDir = common_1.JSPM_CACHE_DIR;
79 if ('strictSSL' in config === false)
80 config.strictSSL = global_config_file_1.default.get('strictSSL');
81 return config;
82}
83class Project {
84 constructor(projectPath, options) {
85 this.projectPath = projectPath;
86 if (!hasGit)
87 throw new common_1.JspmUserError(`${common_1.bold('git')} is not installed in path. You can install git from http://git-scm.com/downloads.`);
88 const config = applyDefaultConfiguration(options);
89 // is this running as a CLI or API?
90 this.cli = config.cli;
91 this.log = this.cli ? new CLILogger() : new APILogger();
92 if (projectPath === api_1.JSPM_GLOBAL_PATH)
93 this.checkGlobalBin();
94 // if (process.env.globalJspm === 'true')
95 // this.log.warn(`Running jspm globally, it is advisable to locally install jspm via ${bold(`npm install jspm --save-dev`)}.`);
96 this.defaultRegistry = config.defaultRegistry;
97 // hardcoded for now (pending jspm 3...)
98 this.defaultRegistry = 'npm';
99 mkdirp.sync(projectPath);
100 this.config = new config_1.default(projectPath, this);
101 this.globalConfig = global_config_file_1.default;
102 this.confirm = this.cli ? ui_1.confirm : (_msg, def) => Promise.resolve(typeof def === 'boolean' ? def : undefined);
103 this.input = this.cli ? ui_1.input : (_msg, def) => Promise.resolve(typeof def === 'string' ? def : undefined);
104 this.userInput = config.userInput;
105 this.offline = config.offline;
106 this.preferOffline = config.preferOffline;
107 this.cacheDir = config.cacheDir;
108 this.fetch = new fetch_1.default(this);
109 this.registryManager = new registry_manager_1.default({
110 cacheDir: this.cacheDir,
111 defaultRegistry: this.defaultRegistry,
112 Cache: cache_1.default,
113 timeouts: {
114 resolve: config.timeouts.resolve,
115 download: config.timeouts.download
116 },
117 offline: this.offline,
118 preferOffline: this.preferOffline,
119 userInput: this.userInput,
120 strictSSL: config.strictSSL,
121 log: this.log,
122 confirm: this.confirm,
123 input: this.input,
124 fetch: this.fetch,
125 registries: config.registries
126 });
127 // load registries upfront
128 // (strictly we should save registry configuration when a new registry appears)
129 this.registryManager.loadEndpoints();
130 cjs_convert_1.init();
131 this.installer = new install_1.Installer(this);
132 }
133 checkGlobalBin() {
134 if (this.checkedGlobalBin)
135 return;
136 const globalBin = path.join(api_1.JSPM_GLOBAL_PATH, 'jspm_packages', '.bin');
137 if (process.env[common_1.PATH].indexOf(globalBin) === -1)
138 this.log.warn(`The global jspm bin folder ${common_1.highlight(globalBin)} is not currently in your PATH, add this for native jspm bin support.`);
139 this.checkedGlobalBin = true;
140 }
141 dispose() {
142 return Promise.all([
143 this.config.dispose(),
144 this.registryManager.dispose(),
145 cjs_convert_1.dispose()
146 ]);
147 }
148 async save() {
149 return await this.config.save();
150 }
151 /*
152 * Main API methods
153 */
154 async update(selectors, opts) {
155 const taskEnd = this.log.taskStart('Updating...');
156 try {
157 var changed = await this.installer.update(selectors, opts);
158 }
159 finally {
160 taskEnd();
161 }
162 // NB install state change logging!
163 if (changed)
164 this.log.ok('Update complete.');
165 else
166 this.log.ok('Already up to date.');
167 }
168 async install(installs, opts = {}) {
169 const taskEnd = this.log.taskStart('Installing...');
170 try {
171 await runHook(this, 'preinstall');
172 if (installs.length === 0) {
173 opts.lock = true;
174 if (opts.latest) {
175 opts.latest = false;
176 this.log.warn(`${common_1.bold('--latest')} flag does not apply to package lock install.`);
177 }
178 }
179 var changed = await this.installer.install(installs, opts);
180 await runHook(this, 'postinstall');
181 }
182 finally {
183 taskEnd();
184 }
185 // NB install state change logging!
186 if (changed)
187 this.log.ok('Install complete.');
188 else
189 this.log.ok('Already installed.');
190 }
191 async uninstall(names) {
192 const taskEnd = this.log.taskStart('Uninstalling...');
193 try {
194 await this.installer.uninstall(names);
195 }
196 finally {
197 taskEnd();
198 }
199 this.log.ok('Uninstalled successfully.');
200 }
201 async checkout(names) {
202 const taskEnd = this.log.taskStart('Checking out...');
203 try {
204 await this.installer.checkout(names);
205 }
206 finally {
207 taskEnd();
208 }
209 }
210 async link(pkg, source, opts) {
211 const taskEnd = this.log.taskStart('Linking...');
212 try {
213 await runHook(this, 'preinstall');
214 var changed = await this.installer.link(pkg, source, opts);
215 await runHook(this, 'postinstall');
216 }
217 finally {
218 taskEnd();
219 }
220 if (changed)
221 this.log.ok('Linked Successfully.');
222 else
223 this.log.ok('Already linked.');
224 }
225 async clean() {
226 const taskEnd = this.log.taskStart('Cleaning...');
227 try {
228 await this.installer.clean(true);
229 }
230 finally {
231 taskEnd();
232 }
233 this.log.ok('Project cleaned successfully.');
234 }
235 /*
236 async resolve (name: string, parentName: string) {
237 let loader = getLoader(this);
238 if (parentName)
239 parentName = await loader.resolve(parentName);
240
241 let resolved = await loader.resolve(name, parentName);
242 return toCleanPath(resolved);
243 }
244
245 resolveSync (name: string, parentName: string) {
246 let loader = getLoader(this);
247 if (parentName)
248 parentName = loader.resolveSync(parentName);
249
250 let resolved = loader.resolveSync(name, parentName);
251 return toCleanPath(resolved);
252 }
253 */
254 async init(basePath) {
255 if (basePath)
256 process.env.jspmConfigPath = path.resolve(basePath, 'package.json');
257 let relBase = path.relative(process.cwd(), path.dirname(process.env.jspmConfigPath || ''));
258 if (relBase !== '')
259 this.log.msg(`Initializing package at ${common_1.highlight(relBase)}\nUse ${common_1.bold(`jspm init .`)} to intialize into the current folder.`);
260 /* await this.config.load(true);
261 await this.config.save();
262
263 this.log('');
264 this.ok(`package.json at %${path.relative(process.cwd(), config.pjsonPath)}\n` +
265 `Config at %${path.relative(process.cwd(), config.pjson.configFile)}%` +
266 (config.loader.devFile ? ', %' + path.relative(process.cwd(), config.pjson.configFileDev) + '%' : '') +
267 (config.loader.browserFile ? ', %' + path.relative(process.cwd(), config.pjson.configFileBrowser) + '%' : '') +
268 (config.loader.nodeFile ? ', %' + path.relative(process.cwd(), config.pjson.configFileNode) + '%' : ''));*/
269 }
270 async registryConfig(name) {
271 return this.registryManager.configure(name);
272 }
273 async clearCache() {
274 await new Promise((resolve, reject) => rimraf(this.cacheDir, err => err ? reject(err) : resolve()));
275 this.log.warn(`Global cache cleared. ${common_1.underline(`All jspm projects for this system user will now have broken symlinks due to the shared global package cache.`)}`);
276 this.log.info(`${common_1.bold(`jspm install <packageName> -f`)} is equivalent to running a cache clear for that specific install tree.`);
277 this.log.info(`Please post an issue if you suspect the cache isn't invalidating properly.`);
278 }
279 async run(name, args) {
280 const scripts = this.config.pjson.scripts;
281 const script = scripts[name];
282 if (!script)
283 throw new common_1.JspmUserError(`No package.json ${common_1.highlight('"scripts"')} entry for command ${common_1.bold(name)}`);
284 const doPrePost = !name.startsWith('pre') && !name.startsWith('post');
285 const cmds = [];
286 if (doPrePost) {
287 const pre = scripts[`pre${name}`];
288 if (pre)
289 cmds.push(pre);
290 cmds.push(script);
291 const post = scripts[`post${name}`];
292 if (post)
293 cmds.push(post);
294 }
295 else {
296 cmds.push(script);
297 }
298 // before running commands dispose the configuration
299 this.config.dispose();
300 this.config = undefined;
301 let exitCode = 0;
302 await Promise.all(cmds.map(async (cmd) => {
303 if (args.length)
304 cmd += joinArgs(args);
305 cmd = cmd.replace('npm ', 'jspm ');
306 const cmdCode = await run_cmd_1.runCmd(cmd, this.projectPath);
307 if (cmdCode !== 0)
308 exitCode = cmdCode;
309 }));
310 return exitCode;
311 }
312}
313exports.Project = Project;
314const dblQuoteRegEx = /"/g;
315function joinArgs(args) {
316 return args.reduce((str, arg) => `${str} "${arg.replace(dblQuoteRegEx, '\\"')}"`, '');
317}
318async function runHook(project, name) {
319 var hooks = project.config.pjson.hooks;
320 if (!hooks || !hooks[name])
321 return;
322 try {
323 let m = require(hooks[name]);
324 if (!m.default || typeof m.default !== 'function')
325 throw new Error(`Hook ${common_1.bold(name)} doesn't contain a default export hook function.`);
326 await m.default();
327 }
328 catch (e) {
329 project.log.err(`Error running ${common_1.bold(name)} hook.`);
330 project.log.err(e.stack || e);
331 }
332}
333exports.runHook = runHook;
334class APILogger extends events.EventEmitter {
335 newline() { }
336 msg(msg) {
337 this.emit('msg', msg);
338 }
339 errMsg(msg) {
340 this.emit('errMsg', msg);
341 }
342 err(msg) {
343 this.emit('err', msg);
344 }
345 debug(msg) {
346 this.emit('debug', msg);
347 }
348 info(msg) {
349 this.emit('info', msg);
350 }
351 warn(msg) {
352 this.emit('warn', msg);
353 }
354 ok(msg) {
355 this.emit('ok', msg);
356 }
357 taskStart(name) {
358 this.emit('taskStart', name);
359 return () => this.taskEnd(name);
360 }
361 taskEnd(name) {
362 this.emit('taskEnd', name);
363 }
364}
365;
366class CLILogger {
367 constructor() {
368 this.tasks = [];
369 this.lastTask = undefined;
370 }
371 newline() {
372 ui_1.log('');
373 }
374 msg(msg) {
375 ui_1.log(msg);
376 }
377 errMsg(msg) {
378 if (msg instanceof Error) {
379 if (msg.hideStack)
380 msg = msg.message;
381 else
382 msg = msg.stack || msg && msg.toString();
383 }
384 ui_1.logErr(msg);
385 }
386 err(msg) {
387 if (msg instanceof Error) {
388 if (msg.hideStack)
389 msg = msg.message;
390 else
391 msg = msg.stack || msg && msg.toString();
392 }
393 ui_1.log(msg, ui_1.LogType.err);
394 }
395 debug(msg) {
396 ui_1.log(msg, ui_1.LogType.debug);
397 }
398 info(msg) {
399 ui_1.log(msg, ui_1.LogType.info);
400 }
401 warn(msg) {
402 ui_1.log(msg, ui_1.LogType.warn);
403 }
404 ok(msg) {
405 ui_1.log(msg, ui_1.LogType.ok);
406 }
407 taskStart(name) {
408 this.tasks.push(name);
409 ui_1.log(this.tasks[this.tasks.length - 1], ui_1.LogType.status);
410 if (this.tasks.length === 1)
411 ui_1.startSpinner();
412 // allow debug log state to expand status
413 if (ui_1.logLevel === ui_1.LogType.debug && this.lastTask)
414 ui_1.log(this.lastTask, ui_1.LogType.debug);
415 this.lastTask = name;
416 return this.taskEnd.bind(this, name);
417 }
418 taskEnd(name) {
419 const taskIndex = this.tasks.indexOf(name);
420 if (taskIndex === -1)
421 return;
422 this.tasks.splice(taskIndex, 1);
423 if (this.tasks.length)
424 ui_1.log(this.tasks[this.tasks.length - 1], ui_1.LogType.status);
425 else
426 ui_1.stopSpinner();
427 if (ui_1.logLevel === ui_1.LogType.debug && this.lastTask && this.lastTask !== this.tasks[this.tasks.length - 1]) {
428 ui_1.log(this.lastTask, ui_1.LogType.debug);
429 this.lastTask = undefined;
430 }
431 }
432}
433;
434//# sourceMappingURL=project.js.map
\No newline at end of file