mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-21 22:34:21 +08:00
188 lines
4.9 KiB
JavaScript
188 lines
4.9 KiB
JavaScript
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const RSVP = require('rsvp');
|
|
const chalk = require('chalk');
|
|
const EditFileDiff = require('./edit-file-diff');
|
|
const EOL = require('os').EOL;
|
|
const rxEOL = new RegExp(EOL, 'g');
|
|
const isBinaryFile = require('isbinaryfile').sync;
|
|
const canEdit = require('../utilities/open-editor').canEdit;
|
|
|
|
const Promise = RSVP.Promise;
|
|
const readFile = RSVP.denodeify(fs.readFile);
|
|
const lstat = RSVP.denodeify(fs.stat);
|
|
|
|
function processTemplate(content, context) {
|
|
let options = {
|
|
evaluate: /<%([\s\S]+?)%>/g,
|
|
interpolate: /<%=([\s\S]+?)%>/g,
|
|
escape: /<%-([\s\S]+?)%>/g,
|
|
};
|
|
return require('lodash/template')(content, options)(context);
|
|
}
|
|
|
|
function diffHighlight(line) {
|
|
if (line[0] === '+') {
|
|
return chalk.green(line);
|
|
} else if (line[0] === '-') {
|
|
return chalk.red(line);
|
|
} else if (line.match(/^@@/)) {
|
|
return chalk.cyan(line);
|
|
} else {
|
|
return line;
|
|
}
|
|
}
|
|
|
|
class FileInfo {
|
|
constructor(options) {
|
|
this.action = options.action;
|
|
this.outputBasePath = options.outputBasePath;
|
|
this.outputPath = options.outputPath;
|
|
this.displayPath = options.displayPath;
|
|
this.inputPath = options.inputPath;
|
|
this.templateVariables = options.templateVariables;
|
|
this.ui = options.ui;
|
|
}
|
|
|
|
confirmOverwrite(path) {
|
|
let promptOptions = {
|
|
type: 'expand',
|
|
name: 'answer',
|
|
default: false,
|
|
message: `${chalk.red('Overwrite')} ${path}?`,
|
|
choices: [
|
|
{ key: 'y', name: 'Yes, overwrite', value: 'overwrite' },
|
|
{ key: 'n', name: 'No, skip', value: 'skip' },
|
|
],
|
|
};
|
|
|
|
let outputPathIsFile = false;
|
|
try { outputPathIsFile = fs.statSync(this.outputPath).isFile(); } catch (err) { /* ignore */ }
|
|
|
|
let canDiff = (
|
|
!isBinaryFile(this.inputPath) && (
|
|
!outputPathIsFile ||
|
|
!isBinaryFile(this.outputPath)
|
|
)
|
|
);
|
|
|
|
if (canDiff) {
|
|
promptOptions.choices.push({ key: 'd', name: 'Diff', value: 'diff' });
|
|
|
|
if (canEdit()) {
|
|
promptOptions.choices.push({ key: 'e', name: 'Edit', value: 'edit' });
|
|
}
|
|
}
|
|
|
|
return this.ui.prompt(promptOptions)
|
|
.then(response => response.answer);
|
|
}
|
|
|
|
displayDiff() {
|
|
let info = this,
|
|
jsdiff = require('diff');
|
|
return RSVP.hash({
|
|
input: this.render(),
|
|
output: readFile(info.outputPath),
|
|
}).then(result => {
|
|
let diff = jsdiff.createPatch(
|
|
info.outputPath, result.output.toString().replace(rxEOL, '\n'), result.input.replace(rxEOL, '\n')
|
|
);
|
|
let lines = diff.split('\n');
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
info.ui.write(
|
|
diffHighlight(lines[i] + EOL)
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
render() {
|
|
let path = this.inputPath,
|
|
context = this.templateVariables;
|
|
if (!this.rendered) {
|
|
this.rendered = readFile(path)
|
|
.then(content => lstat(path)
|
|
.then(fileStat => {
|
|
if (isBinaryFile(content, fileStat.size)) {
|
|
return content;
|
|
} else {
|
|
try {
|
|
return processTemplate(content.toString(), context);
|
|
} catch (err) {
|
|
err.message += ` (Error in blueprint template: ${path})`;
|
|
throw err;
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
return this.rendered;
|
|
}
|
|
|
|
checkForConflict() {
|
|
return new Promise((resolve, reject) => {
|
|
fs.exists(this.outputPath, (doesExist, error) => {
|
|
if (error) {
|
|
reject(error);
|
|
return;
|
|
}
|
|
let result;
|
|
|
|
if (doesExist) {
|
|
result = RSVP.hash({
|
|
input: this.render(),
|
|
output: readFile(this.outputPath),
|
|
}).then(result => {
|
|
let type;
|
|
if (result.input.toString().replace(rxEOL, '\n') === result.output.toString().replace(rxEOL, '\n')) {
|
|
type = 'identical';
|
|
} else {
|
|
type = 'confirm';
|
|
}
|
|
return type;
|
|
});
|
|
} else {
|
|
result = 'none';
|
|
}
|
|
|
|
resolve(result);
|
|
});
|
|
});
|
|
}
|
|
|
|
confirmOverwriteTask() {
|
|
let info = this;
|
|
|
|
return function() {
|
|
return new Promise((resolve, reject) => {
|
|
function doConfirm() {
|
|
info.confirmOverwrite(info.displayPath).then(action => {
|
|
if (action === 'diff') {
|
|
info.displayDiff().then(doConfirm, reject);
|
|
} else if (action === 'edit') {
|
|
let editFileDiff = new EditFileDiff({ info });
|
|
editFileDiff.edit().then(() => {
|
|
info.action = action;
|
|
resolve(info);
|
|
}).catch(() => {
|
|
doConfirm().finally(() => {
|
|
resolve(info);
|
|
});
|
|
});
|
|
} else {
|
|
info.action = action;
|
|
resolve(info);
|
|
}
|
|
}, reject);
|
|
}
|
|
|
|
doConfirm();
|
|
});
|
|
}.bind(this);
|
|
}
|
|
}
|
|
|
|
module.exports = FileInfo;
|