Skip to content
This repository has been archived by the owner on Dec 5, 2020. It is now read-only.

Commit

Permalink
feat: teach gulp extend to accept a package URL (#266)
Browse files Browse the repository at this point in the history
  • Loading branch information
wincent committed Apr 30, 2019
1 parent 920648f commit f18bd61
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 14 deletions.
14 changes: 12 additions & 2 deletions packages/liferay-theme-tasks/lib/prompts/extend_prompt.js
Expand Up @@ -12,6 +12,7 @@ const inquirer = require('inquirer');
const GlobalModulePrompt = require('./global_module_prompt');
const lfrThemeConfig = require('../liferay_theme_config');
const NPMModulePrompt = require('./npm_module_prompt');
const URLPackagePrompt = require('./url_package_prompt');
const promptUtil = require('./prompt_util');
const themeFinder = require('../theme_finder');

Expand Down Expand Up @@ -141,12 +142,17 @@ class ExtendPrompt {
if (themeSource === 'global') {
GlobalModulePrompt.prompt(
config,
_.bind(this._afterPromptModule, this)
this._afterPromptModule.bind(this)
);
} else if (themeSource === 'npm') {
NPMModulePrompt.prompt(
config,
_.bind(this._afterPromptModule, this)
this._afterPromptModule.bind(this)
);
} else if (themeSource === 'url') {
URLPackagePrompt.prompt(
config,
this._afterPromptModule.bind(this)
);
}
}
Expand Down Expand Up @@ -217,6 +223,10 @@ class ExtendPrompt {
name: 'Search npm registry (published modules)',
value: 'npm',
},
{
name: 'Specify a package URL',
value: 'url',
},
];

if (extendType === 'theme') {
Expand Down
59 changes: 59 additions & 0 deletions packages/liferay-theme-tasks/lib/prompts/url_package_prompt.js
@@ -0,0 +1,59 @@
/**
* © 2017 Liferay, Inc. <https://liferay.com>
*
* SPDX-License-Identifier: MIT
*/

const inquirer = require('inquirer');
const {URL} = require('url');

const themeFinder = require('../theme_finder');

class URLPackagePrompt {
constructor(...args) {
this.init(...args);
}

init(config, cb) {
this.done = cb;

inquirer.prompt(
[
{
message: 'Enter the URL for the package:',
name: 'packageURL',
type: 'input',
validate: this._validatePackageURL,
},
],
this._afterPrompt.bind(this)
);
}

_validatePackageURL(packageURL, _answers) {
try {
new URL(packageURL);
} catch (err) {
return `"${packageURL}" is not a valid URL`;
}

return true;
}

_afterPrompt(answers) {
const config = themeFinder.getLiferayThemeModuleFromURL(answers.packageURL);
answers.module = config.name;
answers.modules = {
[config.name]: Object.assign(
{},
config,
{realPath: answers.packageURL},
),
};
this.done(answers);
}
}

URLPackagePrompt.prompt = (config, cb) => new URLPackagePrompt(config, cb);

module.exports = URLPackagePrompt;
103 changes: 91 additions & 12 deletions packages/liferay-theme-tasks/lib/theme_finder.js
Expand Up @@ -6,11 +6,15 @@

const _ = require('lodash');
const async = require('async');
const child_process = require('child_process');
const fs = require('fs');
const globby = require('globby');
const npmKeyword = require('npm-keyword');
const os = require('os');
const packageJson = require('package-json');
const path = require('path');
const spawn = require('cross-spawn');
const {URL} = require('url');

const lfrThemeConfig = require('./liferay_theme_config');

Expand All @@ -22,10 +26,7 @@ function getLiferayThemeModule(name, cb) {
name,
},
(err, pkg) => {
if (
(pkg && !pkg.liferayTheme) ||
(pkg && !_.includes(pkg.keywords, 'liferay-theme'))
) {
if (!isLiferayTheme(pkg)) {
pkg = null;

err = new Error(
Expand All @@ -38,6 +39,51 @@ function getLiferayThemeModule(name, cb) {
);
}

/**
* Given a package URL, attempts to download it, extract the package.json, and
* validate it.
*/
function getLiferayThemeModuleFromURL(url, cb) {
try {
// Throw if `url` is invalid.
new URL(url);

let config;

// Install the package in a temporary directory in order to get
// the package.json.
withScratchDirectory(() => {
child_process.spawnSync('npm', ['init', '-y']);

// Ideally, we wouldn't install any dependencies at all, but this is
// the closest we can get (production only, skipping optional
// dependencies).
child_process.spawnSync('npm', [
'install',
'--ignore-scripts',
'--no-optional',
'--production',
url,
]);

// Just in case package name doesn't match URL basename, read it.
const {dependencies} = JSON.parse(fs.readFileSync('package.json'));
const themeName = Object.keys(dependencies)[0];

const json = path.join('node_modules', themeName, 'package.json');
config = JSON.parse(fs.readFileSync(json));
});

if (!isLiferayTheme(config)) {
throw new Error(`URL ${url} is not a liferay-theme`);
} else {
return config;
}
} catch (err) {
cb(err);
}
}

function getLiferayThemeModules(config, cb) {
if (_.isUndefined(cb)) {
cb = config;
Expand Down Expand Up @@ -79,16 +125,41 @@ function getLiferayThemeModules(config, cb) {
});
}

module.exports = {getLiferayThemeModule, getLiferayThemeModules};

const LiferayThemeModuleStatus = {
NO_PACKAGE_JSON: 'NO_PACKAGE_JSON',
NO_LIFERAY_THEME: 'NO_LIFERAY_THEME',
TARGET_VERSION_DOES_NOT_MATCH: 'TARGET_VERSION_DOES_NOT_MATCH',
THEMELET_FLAG_DOES_NOT_MATCH: 'THEMELET_FLAG_DOES_NOT_MATCH',
OK: 'OK',
module.exports = {
getLiferayThemeModule,
getLiferayThemeModuleFromURL,
getLiferayThemeModules,
};

/**
* Execute `cb()` in the context of a temporary directory.
*
* Note that `cb()` should be entirely synchronous, because clean-up is
* performed as soon as it returns.
*/
function withScratchDirectory(cb) {
const template = path.join(os.tmpdir(), 'theme-finder-');
const directory = fs.mkdtempSync(template);

const cwd = process.cwd();

try {
process.chdir(directory);
cb();
} finally {
process.chdir(cwd);
}
}

function isLiferayTheme(config) {
return (
config &&
config.liferayTheme &&
config.keywords &&
config.keywords.indexOf('liferay-theme') !== -1
);
}

function reportDiscardedModules(moduleResults, outcome, message) {
if (moduleResults[outcome]) {
// eslint-disable-next-line no-console
Expand Down Expand Up @@ -172,6 +243,14 @@ function getPackageJSON(theme, cb) {
.catch(cb);
}

const LiferayThemeModuleStatus = {
NO_PACKAGE_JSON: 'NO_PACKAGE_JSON',
NO_LIFERAY_THEME: 'NO_LIFERAY_THEME',
TARGET_VERSION_DOES_NOT_MATCH: 'TARGET_VERSION_DOES_NOT_MATCH',
THEMELET_FLAG_DOES_NOT_MATCH: 'THEMELET_FLAG_DOES_NOT_MATCH',
OK: 'OK',
};

function getLiferayThemeModuleStatus(pkg, themelet) {
if (pkg) {
const liferayTheme = pkg.liferayTheme;
Expand Down

0 comments on commit f18bd61

Please sign in to comment.