2020-11-15 16:18:46 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const Generator = require('yeoman-generator');
|
|
|
|
const chalk = require('chalk');
|
|
|
|
const yosay = require('yosay');
|
2021-01-04 20:45:43 +02:00
|
|
|
const slugify = require('slugify');
|
2020-11-15 16:18:46 +02:00
|
|
|
|
2020-11-19 17:57:47 +02:00
|
|
|
function mergePackageKey(parentKey, source, dest) {
|
|
|
|
const output = Object.assign({}, dest);
|
|
|
|
|
|
|
|
for (const k in source) {
|
2021-01-04 21:32:30 +02:00
|
|
|
if (k === 'keywords' && !Array.isArray(output[k])) {
|
|
|
|
// Fix an earlier bugs where keywords were set to an empty object
|
|
|
|
output[k] = source[k];
|
|
|
|
} else if (k === 'keywords') {
|
|
|
|
// For keywords, make sure to add the "joplin-plugin" one
|
|
|
|
if (!output['keywords']) output['keywords'] = [];
|
|
|
|
if (output['keywords'].indexOf('joplin-plugin') < 0) output['keywords'].push('joplin-plugin');
|
|
|
|
} else if (!(k in output)) {
|
2020-11-19 17:57:47 +02:00
|
|
|
// If the key doesn't exist in the destination, add it
|
|
|
|
output[k] = source[k];
|
|
|
|
} else if (parentKey === 'devDependencies') {
|
|
|
|
// If we are dealing with the dependencies, overwrite with the
|
|
|
|
// version from source.
|
|
|
|
output[k] = source[k];
|
2021-01-03 15:21:48 +02:00
|
|
|
} else if (typeof source[k] === 'object' && !Array.isArray(source[k]) && source[k] !== null) {
|
2020-11-19 17:57:47 +02:00
|
|
|
// If it's an object, recursively process it
|
|
|
|
output[k] = mergePackageKey(k, source[k], output[k]);
|
|
|
|
} else {
|
|
|
|
// Otherwise, the default is to preserve the destination key
|
|
|
|
output[k] = dest[k];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2021-01-05 20:18:40 +02:00
|
|
|
function mergeIgnoreFile(source, dest) {
|
|
|
|
const output = source.split('\n').concat(dest.split('\n'));
|
2021-01-05 20:28:12 +02:00
|
|
|
|
2021-01-05 20:18:40 +02:00
|
|
|
return output.filter(function(item, pos) {
|
|
|
|
if (!item) return true; // Leave blank lines
|
|
|
|
return output.indexOf(item) === pos;
|
2021-01-05 20:28:12 +02:00
|
|
|
}).join('\n');
|
2021-01-05 20:18:40 +02:00
|
|
|
}
|
|
|
|
|
2021-01-04 20:45:43 +02:00
|
|
|
function packageNameFromPluginName(pluginName) {
|
2021-01-05 14:09:43 +02:00
|
|
|
// Package name is limited to 214 characters
|
2021-01-04 20:45:43 +02:00
|
|
|
return `joplin-plugin-${slugify(pluginName, {
|
|
|
|
lower: true,
|
2021-01-05 14:09:43 +02:00
|
|
|
})}`.substr(0, 214);
|
2021-01-04 20:45:43 +02:00
|
|
|
}
|
|
|
|
|
2020-11-15 16:18:46 +02:00
|
|
|
module.exports = class extends Generator {
|
2020-11-18 12:17:27 +02:00
|
|
|
|
|
|
|
constructor(args, opts) {
|
|
|
|
super(args, opts);
|
|
|
|
|
|
|
|
this.option('silent');
|
|
|
|
this.option('update');
|
|
|
|
|
|
|
|
if (this.options.update) {
|
|
|
|
// When updating, overwrite files without prompting
|
|
|
|
this.conflicter.force = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async prompting() {
|
2021-01-05 20:28:12 +02:00
|
|
|
this.log(yosay(`Welcome to the fine ${chalk.red('Joplin Plugin')} generator!`));
|
2020-11-15 16:18:46 +02:00
|
|
|
|
2020-11-18 12:17:27 +02:00
|
|
|
if (this.options.update && !this.options.silent) {
|
|
|
|
const answers = await this.prompt([
|
|
|
|
{
|
|
|
|
type: 'confirm',
|
|
|
|
name: 'proceed',
|
|
|
|
message: [
|
2021-01-05 19:57:42 +02:00
|
|
|
'Updating will overwrite the config-related files. It will not change the',
|
|
|
|
' content of /src or README.md. If you have made any changes to some of the',
|
|
|
|
' config files make sure your code is under version control so that you can',
|
|
|
|
' inspect the diff and re-apply your changes if needed. Do you want to proceed?',
|
2020-11-18 12:17:27 +02:00
|
|
|
].join('\n'),
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
|
|
|
|
if (!answers.proceed) {
|
|
|
|
this.log('');
|
|
|
|
this.log('Operation was cancelled and no changes was made');
|
|
|
|
process.exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-15 16:18:46 +02:00
|
|
|
const prompts = [
|
2020-11-17 20:26:24 +02:00
|
|
|
{
|
|
|
|
type: 'input',
|
|
|
|
name: 'pluginId',
|
2021-01-05 14:09:43 +02:00
|
|
|
message: 'Plugin ID\n Must be a globally unique ID such as "com.example.MyPlugin" or a UUID:',
|
2020-11-17 20:26:24 +02:00
|
|
|
},
|
2020-11-15 16:18:46 +02:00
|
|
|
{
|
|
|
|
type: 'input',
|
|
|
|
name: 'pluginName',
|
2021-01-05 14:09:43 +02:00
|
|
|
message: 'Plugin name\n User-friendly string which will be displayed in UI:',
|
2020-11-15 16:18:46 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'input',
|
|
|
|
name: 'pluginDescription',
|
|
|
|
message: 'Plugin description:',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'input',
|
|
|
|
name: 'pluginAuthor',
|
|
|
|
message: 'Author:',
|
|
|
|
},
|
2021-01-04 20:45:43 +02:00
|
|
|
{
|
|
|
|
type: 'input',
|
|
|
|
name: 'pluginRepositoryUrl',
|
|
|
|
message: 'Repository URL:',
|
|
|
|
},
|
2020-11-15 16:18:46 +02:00
|
|
|
{
|
|
|
|
type: 'input',
|
|
|
|
name: 'pluginHomepageUrl',
|
|
|
|
message: 'Homepage URL:',
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
2020-11-18 12:17:27 +02:00
|
|
|
if (this.options.update) {
|
|
|
|
const props = {};
|
|
|
|
for (const prompt of prompts) {
|
|
|
|
props[prompt.name] = '';
|
|
|
|
}
|
2021-01-05 14:09:43 +02:00
|
|
|
props.packageName = '';
|
|
|
|
|
|
|
|
this.props = props;
|
2020-11-18 12:17:27 +02:00
|
|
|
} else {
|
2021-01-05 14:09:43 +02:00
|
|
|
const initialProps = await this.prompt(prompts);
|
|
|
|
|
|
|
|
const defaultPackageName = packageNameFromPluginName(initialProps.pluginName);
|
|
|
|
|
|
|
|
const derivedProps = await this.prompt([
|
|
|
|
{
|
|
|
|
type: 'input',
|
|
|
|
name: 'packageName',
|
|
|
|
message: `The npm package will be named: "${defaultPackageName}"\n Press ENTER to keep this default, or type a name to change it:`,
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
|
|
|
|
if (!derivedProps.packageName) derivedProps.packageName = defaultPackageName;
|
|
|
|
|
|
|
|
this.props = Object.assign({}, initialProps, derivedProps);
|
2020-11-18 12:17:27 +02:00
|
|
|
}
|
2020-11-15 16:18:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
writing() {
|
|
|
|
// Due to WONTFIX bug in npm, which treats .gitignore and pakage.json in a special way,
|
|
|
|
// we need to give them a different name in the templates dir and then rename them
|
|
|
|
// when installing.
|
|
|
|
// https://github.com/npm/npm/issues/3763
|
|
|
|
|
|
|
|
const files = [
|
|
|
|
'.gitignore_TEMPLATE',
|
2021-01-04 20:45:43 +02:00
|
|
|
'.npmignore_TEMPLATE',
|
|
|
|
'GENERATOR_DOC.md',
|
2020-11-15 16:18:46 +02:00
|
|
|
'package_TEMPLATE.json',
|
|
|
|
'tsconfig.json',
|
|
|
|
'webpack.config.js',
|
2020-11-18 12:17:27 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
const noUpdateFiles = [
|
2020-11-15 16:18:46 +02:00
|
|
|
'src/index.ts',
|
|
|
|
'src/manifest.json',
|
2021-01-05 19:57:42 +02:00
|
|
|
'README.md',
|
2020-11-15 16:18:46 +02:00
|
|
|
];
|
|
|
|
|
2020-11-18 12:17:27 +02:00
|
|
|
const allFiles = files.concat(noUpdateFiles);
|
|
|
|
|
|
|
|
for (const file of allFiles) {
|
|
|
|
if (this.options.update && noUpdateFiles.includes(file)) continue;
|
|
|
|
|
2020-11-15 16:18:46 +02:00
|
|
|
const destFile = file.replace(/_TEMPLATE/, '');
|
2020-11-19 17:57:47 +02:00
|
|
|
const destFilePath = this.destinationPath(destFile);
|
|
|
|
|
|
|
|
if (this.options.update && destFile === 'package.json' && this.fs.exists(destFilePath)) {
|
|
|
|
const destContent = this.fs.readJSON(destFilePath);
|
|
|
|
|
|
|
|
this.fs.copy(
|
|
|
|
this.templatePath(file),
|
|
|
|
destFilePath, {
|
|
|
|
process: (sourceBuffer) => {
|
|
|
|
const sourceContent = JSON.parse(sourceBuffer.toString());
|
|
|
|
const newContent = mergePackageKey(null, sourceContent, destContent);
|
|
|
|
return JSON.stringify(newContent, null, 2);
|
|
|
|
},
|
|
|
|
}
|
|
|
|
);
|
2021-01-05 20:18:40 +02:00
|
|
|
} else if (this.options.update && (destFile === '.gitignore' || destFile === '.npmignore') && this.fs.exists(destFilePath)) {
|
|
|
|
const destContent = this.fs.read(destFilePath);
|
|
|
|
|
|
|
|
this.fs.copy(
|
|
|
|
this.templatePath(file),
|
|
|
|
destFilePath, {
|
|
|
|
process: (sourceBuffer) => {
|
|
|
|
return mergeIgnoreFile(sourceBuffer.toString(), destContent);
|
|
|
|
},
|
|
|
|
}
|
|
|
|
);
|
2020-11-19 17:57:47 +02:00
|
|
|
} else {
|
|
|
|
this.fs.copyTpl(
|
|
|
|
this.templatePath(file),
|
|
|
|
destFilePath,
|
|
|
|
this.props
|
|
|
|
);
|
|
|
|
}
|
2020-11-15 16:18:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
this.fs.copy(
|
|
|
|
this.templatePath('api'),
|
|
|
|
this.destinationPath('api')
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
install() {
|
|
|
|
this.installDependencies({
|
|
|
|
npm: true,
|
|
|
|
bower: false,
|
|
|
|
yarn: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|