diff --git a/.gitignore b/.gitignore index 5e5b5337d..25efed593 100644 --- a/.gitignore +++ b/.gitignore @@ -8,9 +8,6 @@ npm-debug.log # code coverage coverage -# build -lib - # tests results **/__tests__/_output* diff --git a/lib/Link/index.js b/lib/Link/index.js new file mode 100644 index 000000000..02a1dfc2f --- /dev/null +++ b/lib/Link/index.js @@ -0,0 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Link = undefined; + +var _chalk = require("chalk"); + +var _chalk2 = _interopRequireDefault(_chalk); + +var _Link = require("../components/Link"); + +var _Link2 = _interopRequireDefault(_Link); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// deprecated +console.log("⚠️ " + _chalk2.default.yellow("'phenomic/lib/Link' reference is deprecated.\n" + "Please use `import { Link } from \"phenomic\" instead`.")); + +exports.Link = _Link2.default; +exports.default = _Link2.default; \ No newline at end of file diff --git a/lib/PageContainer/index.js b/lib/PageContainer/index.js new file mode 100644 index 000000000..cb1d1442c --- /dev/null +++ b/lib/PageContainer/index.js @@ -0,0 +1,20 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _chalk = require("chalk"); + +var _chalk2 = _interopRequireDefault(_chalk); + +var _PageContainer = require("../components/PageContainer"); + +var _PageContainer2 = _interopRequireDefault(_PageContainer); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// deprecated +console.log("⚠️ " + _chalk2.default.yellow("'phenomic/lib/PageContainer' reference is deprecated.\n" + "Please use `import { PageContainer } from \"phenomic\" instead`.")); + +exports.default = _PageContainer2.default; \ No newline at end of file diff --git a/lib/_utils/array-unique/index.js b/lib/_utils/array-unique/index.js new file mode 100644 index 000000000..761103966 --- /dev/null +++ b/lib/_utils/array-unique/index.js @@ -0,0 +1,15 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +// @ flow +// flow is not enabled because of the issue with set and spread +// https://github.com/facebook/flow/issues/1059 + +exports.default = function (array) { + return [].concat(_toConsumableArray(new Set(array))); +}; \ No newline at end of file diff --git a/lib/_utils/beautify-html/index.js b/lib/_utils/beautify-html/index.js new file mode 100644 index 000000000..38e975c1d --- /dev/null +++ b/lib/_utils/beautify-html/index.js @@ -0,0 +1,13 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _jsBeautify = require("js-beautify"); + +exports.default = function (html) { + return (0, _jsBeautify.html)(html, _extends({}, _jsBeautify.default_options, { indent_size: 2 })); +}; \ No newline at end of file diff --git a/lib/_utils/bins.js b/lib/_utils/bins.js new file mode 100644 index 000000000..849cbba63 --- /dev/null +++ b/lib/_utils/bins.js @@ -0,0 +1,13 @@ +"use strict"; + +// ⚠️ Used in postinstall, so es6 - imports - not transpiled + +var resolve = require("path").resolve; + +var platformSuffix = process.platform === "win32" ? ".cmd" : ""; + +module.exports = { + babelNode: resolve("./node_modules/.bin/babel-node" + platformSuffix), + npm: "npm" + platformSuffix, + yarn: "yarn" + platformSuffix +}; \ No newline at end of file diff --git a/lib/_utils/cache/webpack.js b/lib/_utils/cache/webpack.js new file mode 100644 index 000000000..ddf8fa80e --- /dev/null +++ b/lib/_utils/cache/webpack.js @@ -0,0 +1,43 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _path = require("path"); + +var _hardSourceWebpackPlugin = require("hard-source-webpack-plugin"); + +var _hardSourceWebpackPlugin2 = _interopRequireDefault(_hardSourceWebpackPlugin); + +var _nodeObjectHash = require("node-object-hash"); + +var _nodeObjectHash2 = _interopRequireDefault(_nodeObjectHash); + +var _findCacheDir = require("find-cache-dir"); + +var _findCacheDir2 = _interopRequireDefault(_findCacheDir); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.default = function (config) { + if (!config.cache) { + return []; + } + + var cacheDir = (0, _findCacheDir2.default)({ + name: "phenomic/webpack-hard-source-cache/[confighash]/" + }); + + return [new _hardSourceWebpackPlugin2.default({ + cacheDirectory: (0, _path.join)(cacheDir), + recordsPath: (0, _path.join)(cacheDir, "records.json"), + configHash: function configHash(config) { + return new _nodeObjectHash2.default().hash(config); + }, + environmentPaths: { + root: config.cwd, + files: ["package.json", "webpack.config.js"] + } + })]; +}; \ No newline at end of file diff --git a/lib/_utils/catch-links/index.js b/lib/_utils/catch-links/index.js new file mode 100644 index 000000000..80c8b2f69 --- /dev/null +++ b/lib/_utils/catch-links/index.js @@ -0,0 +1,63 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.default = function (elements, cb) { + var eventCallback = catchLinks(cb); + elements.map(function (el) { + return el.addEventListener("click", eventCallback); + }); + + return function () { + elements.map(function (el) { + return el.removeEventListener("click", eventCallback); + }); + }; +}; + +var _url = require("url"); + +var _url2 = _interopRequireDefault(_url); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var catchLinks = function catchLinks(cb) { + return function (ev) { + if (ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.defaultPrevented) { + return; + } + + var anchor = null; + for (var n = ev.target; n.parentNode; n = n.parentNode) { + if (n.nodeName === "A") { + anchor = n; + break; + } + } + if (!anchor) { + return; + } + var href = anchor.getAttribute("href"); + + // Don't intercerpt anchor + if (!href || href.startsWith("#")) { + return; + } + + var u = _url2.default.parse(href); + + if (u.host && u.host !== window.location.host) { + return; + } + + var finalUrl = _url2.default.resolve(window.location.pathname, u.path || "") + (u.hash || ""); + + if (!cb(finalUrl)) { + return; + } + + ev.preventDefault(); + }; +}; \ No newline at end of file diff --git a/lib/_utils/error-formatter/index.js b/lib/_utils/error-formatter/index.js new file mode 100644 index 000000000..77bcdbfcc --- /dev/null +++ b/lib/_utils/error-formatter/index.js @@ -0,0 +1,71 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.help = exports.cwd = undefined; + +var _os = require("os"); + +var _os2 = _interopRequireDefault(_os); + +var _path = require("path"); + +var _path2 = _interopRequireDefault(_path); + +var _chalk = require("chalk"); + +var _chalk2 = _interopRequireDefault(_chalk); + +var _multili = require("multili"); + +var _multili2 = _interopRequireDefault(_multili); + +var _configNode = require("../../builder/webpack/config.node.js"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var normalizeWinPath = function normalizeWinPath(path) { + return path.replace(/\\/g, "\\\\"); +}; + +var cwd = exports.cwd = normalizeWinPath(process.cwd()); +var cache = normalizeWinPath(_configNode.cacheDir); + +var reSep = normalizeWinPath(_path2.default.sep); +var webpackNodeModulesRE = new RegExp("webpack:" + reSep + reSep + "?~", "gm"); +var cleanStaticBuildPathRE = new RegExp(cache + reSep, "gm"); +var cwdRE = new RegExp(cwd + reSep, "g"); +var homeRE = new RegExp(_os2.default.homedir(), "g"); + +var cleanPaths = function cleanPaths(string) { + return string + // normalize windows path + .replace(/\\+g/, "/") + // cleanup + .replace(cleanStaticBuildPathRE, "").replace(webpackNodeModulesRE, "node_modules").replace(cwdRE, "").replace(homeRE, "~"); +}; + +var notDefinedRE = /\s+(.*)( is not defined.*)/gm; +var help = exports.help = _chalk2.default.yellow("\n\n" + "If you are seeing this message during the static build, that means you are" + "probably using an API only available in the browser (such as 'window', " + "'document', 'Element'...). Note that a module can be responsible for this." + "\n" + "In order to prevent this error, you can simply avoid calling the code " + "responsible when the dependency is not available. " + "\n\n" + "Examples:\n" + +/* eslint-disable max-len */ +(0, _multili2.default)("\nFor a single API:\n\n const element = (typeof document !== \"undefined\") ? document.querySelector(\".something\") : null\n\n if (element) {\n // do your thing with your element\n }\n\nFor a module:\n\n const clipboard = (typeof window !== \"undefined\") ? require(\"clipboard\") : null\n\n // then later\n\n if (clipboard) {\n // do your thing using the module\n }\n ") +/* eslint-enable max-len */ +); +var enhanceError = function enhanceError(error) { + if (error.message.match(notDefinedRE) || error.stack.match(notDefinedRE)) { + error.stack += help; + } +}; + +exports.default = function (error) { + error.message = "\n\n" + _chalk2.default.red(error.message) + "\n"; + + // sometimes paths are in message + // eg: errors thrown by webpack loaders/plugin + error.message = cleanPaths(error.message); + error.stack = cleanPaths(error.stack); + enhanceError(error); + + return error; +}; \ No newline at end of file diff --git a/lib/_utils/jsdom/index.js b/lib/_utils/jsdom/index.js new file mode 100644 index 000000000..5e081ce73 --- /dev/null +++ b/lib/_utils/jsdom/index.js @@ -0,0 +1,30 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.default = function (url) { + global.document = _jsdom2.default.jsdom("
", { + url: url + }); + global.window = document.defaultView; + global.navigator = window.navigator; + window.localStorage = window.sessionStorage = { + getItem: function getItem(key) { + return this[key]; + }, + setItem: function setItem(key, value) { + this[key] = value; + }, + removeItem: function removeItem(key) { + delete this[key]; + } + }; +}; + +var _jsdom = require("jsdom"); + +var _jsdom2 = _interopRequireDefault(_jsdom); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } \ No newline at end of file diff --git a/lib/_utils/log/index.js b/lib/_utils/log/index.js new file mode 100644 index 000000000..c5e0620dc --- /dev/null +++ b/lib/_utils/log/index.js @@ -0,0 +1,105 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.plainLog = exports.totalElapsedTime = exports.elapsedTime = exports.formatTime = exports.setTime = exports.setStartTime = exports.start = undefined; + +var _chalk = require("chalk"); + +var _chalk2 = _interopRequireDefault(_chalk); + +var _logSymbols = require("log-symbols"); + +var _logSymbols2 = _interopRequireDefault(_logSymbols); + +var _ora = require("ora"); + +var _ora2 = _interopRequireDefault(_ora); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var symbolOrSuccess = function symbolOrSuccess(symbol) { + return _logSymbols2.default[symbol ? symbol : "success"]; +}; + +// eslint-disable-next-line import/no-mutable-exports + + +var start = exports.start = new Date(); +var lastTime = new Date(); + +// for testing +var setStartTime = exports.setStartTime = function setStartTime(t) { + exports.start = start = t; +}; +var setTime = exports.setTime = function setTime(t) { + lastTime = t; +}; + +var formatTime = exports.formatTime = function formatTime(time) { + return Math.round(time * 100) / 100 + "s"; +}; + +var elapsedTime = exports.elapsedTime = function elapsedTime() { + var now = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Date(); + var time = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : lastTime; + + var diff = (now.getTime() - time.getTime()) / 1000; + lastTime = now; + if (diff < 0.1) { + return ""; + } + + var color = diff < 1 ? _chalk2.default.gray : _chalk2.default.blue; + return color("+" + formatTime(diff)); +}; + +var totalElapsedTime = exports.totalElapsedTime = function totalElapsedTime() { + var now = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Date(); + + return elapsedTime(now, start); +}; + +var plainLog = exports.plainLog = function plainLog(message) { + var symbol = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "info"; + + console.log(_logSymbols2.default[symbol], message); +}; + +exports.default = function (message, action) { + // the space is used to avoid shitty collapsed message from other modules + // that can happen during a loading... + var spinner = (0, _ora2.default)(message + " "); + + if (action && action.then) { + spinner.start(); + // $FlowFixMe when we use .start() above, flow miss the action.then test... + return action.then(function (res) { + spinner.text += elapsedTime(); + spinner.succeed(); + return res; + }, function (res) { + spinner.text += elapsedTime(); + spinner.fail(); + return res; + }); + } + if (typeof action === "function") { + spinner.start(); + try { + var res = action(); + spinner.text += elapsedTime(); + spinner.succeed(); + return res; + } catch (e) { + spinner.text += elapsedTime(); + spinner.fail(); + throw e; + } + } + + // else + spinner.text += elapsedTime(); + spinner.stopAndPersist(symbolOrSuccess(action && String(action))); +}; \ No newline at end of file diff --git a/lib/_utils/normalize-base-url/index.js b/lib/_utils/normalize-base-url/index.js new file mode 100644 index 000000000..59dc2bf57 --- /dev/null +++ b/lib/_utils/normalize-base-url/index.js @@ -0,0 +1,35 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _url = require("url"); + +exports.default = function (baseUrl) { + // ensure trailing slash + if (baseUrl.pathname && !baseUrl.pathname.endsWith("/")) { + baseUrl.pathname = baseUrl.pathname + "/"; + } + + // update baseUrl.href since pathname has been updated + // the usage of the spread operator is to avoid having the "magic" Object + // returned by node (eg: make assertions difficult) + return _extends({}, (0, _url.parse)((0, _url.format)({ + // baseUrl cannot just be passed directly + // https://github.com/facebook/flow/issues/908 + href: baseUrl.href, + protocol: baseUrl.protocol, + slashes: baseUrl.slashes, + auth: baseUrl.auth, + hostname: baseUrl.hostname, + port: baseUrl.port, + host: baseUrl.host, + pathname: baseUrl.pathname, + search: baseUrl.search, + query: baseUrl.query, + hash: baseUrl.hash + }))); +}; \ No newline at end of file diff --git a/lib/_utils/offline/runtime.js b/lib/_utils/offline/runtime.js new file mode 100644 index 000000000..7127fe7e6 --- /dev/null +++ b/lib/_utils/offline/runtime.js @@ -0,0 +1,36 @@ +"use strict"; + +var _runtime = require("offline-plugin/runtime"); + +var _runtime2 = _interopRequireDefault(_runtime); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +console.log("SW Event:", "Installing"); + +// Install ServiceWorker and AppCache in the end since it's not most important +// operation and if main code fails, we do not want it installed + +_runtime2.default.install({ + // you can specify here some code to respond to events + // see here for more informations + // https://www.npmjs.com/package/offline-plugin#runtime + onInstalled: function onInstalled() { + console.log("SW Event:", "onInstalled"); + }, + onUpdating: function onUpdating() { + console.log("SW Event:", "onUpdating"); + }, + onUpdateReady: function onUpdateReady() { + console.log("SW Event:", "onUpdateReady"); + _runtime2.default.applyUpdate(); + }, + onUpdated: function onUpdated() { + console.log("SW Event:", "onUpdated"); + window.location.reload(); + }, + onUninstalled: function onUninstalled() { + console.log("SW Event:", "onUninstalled"); + } +}); +// See webpack configuration file for more offline options \ No newline at end of file diff --git a/lib/_utils/offline/webpack.js b/lib/_utils/offline/webpack.js new file mode 100644 index 000000000..82f6ce5c7 --- /dev/null +++ b/lib/_utils/offline/webpack.js @@ -0,0 +1,106 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.offlineEntry = exports.offlinePlugin = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _path = require("path"); + +var _globby = require("globby"); + +var _offlinePlugin = require("offline-plugin"); + +var _offlinePlugin2 = _interopRequireDefault(_offlinePlugin); + +var _urlJoin = require("url-join"); + +var _urlJoin2 = _interopRequireDefault(_urlJoin); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +var runtimeEntry = (0, _path.resolve)(__dirname, "runtime.js"); + +var offlinePlugin = exports.offlinePlugin = function offlinePlugin(config) { + if (!config.offline) { + return []; + } + var assets = []; + + if (_typeof(config.assets) === "object") { + var configAssets = config.assets; + + assets = (0, _globby.sync)(["**/*"], { + cwd: configAssets.path, + nodir: true + }).map(function (asset) { + return (0, _urlJoin2.default)(configAssets.route, asset); + }); + } + + function preparePatterns(patterns) { + if (!patterns) { + return []; + } + + return patterns.reduce(function (acc, pattern) { + return pattern === ":assets:" ? [].concat(_toConsumableArray(acc), _toConsumableArray(assets)) : [].concat(_toConsumableArray(acc), [pattern]); + }, []); + } + + return [new _offlinePlugin2.default({ + publicPath: config.baseUrl.pathname, + relativePaths: false, + responseStrategy: "network-first", + + // Use this option to explicitely cache files not generated by webpack + externals: ["/"].concat(_toConsumableArray(assets)), + + // every webpack generated assets have hashes, so we can safely + // inform OfflinePlugin about that + safeToUseOptionalCaches: true, + + caches: { + // `main` files will be loaded during SW install + main: preparePatterns(config.offlineConfig.cachePatterns.onInstall), + + // `additional` files are loaded after main section + // and do not prevent SW to install. + additional: preparePatterns(config.offlineConfig.cachePatterns.afterInstall), + + // `optional` files will be cached only when requested + optional: preparePatterns(config.offlineConfig.cachePatterns.onDemand) + }, + // for more advanced usage, see documentation of the plugin + // https://github.com/NekR/offline-plugin/blob/master/docs/caches.md + + excludes: config.offlineConfig.cachePatterns.excludes, + + ServiceWorker: { + events: true, + navigateFallbackURL: "/" + }, + + // Appcache Fallback for browser that does not support Service Worker + // (eg: Safari, IE, Edge...) + AppCache: { + events: true, + FALLBACK: { + "/": "/index.html" + } + } + + })]; +}; + +var offlineEntry = exports.offlineEntry = function offlineEntry(config) { + if (!config.offline) { + return []; + } + + return [runtimeEntry]; +}; \ No newline at end of file diff --git a/lib/_utils/path-to-uri/index.js b/lib/_utils/path-to-uri/index.js new file mode 100644 index 000000000..6e638c80c --- /dev/null +++ b/lib/_utils/path-to-uri/index.js @@ -0,0 +1,16 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _path = require("path"); + +/** + * Normalize path into uri + * Join, replace multiple / or \ to single / + */ +var pathToUri = function pathToUri() { + return _path.join.apply(undefined, arguments).replace(/(\/|\\)+/g, "/"); +}; +exports.default = pathToUri; \ No newline at end of file diff --git a/lib/_utils/serialize/index.js b/lib/_utils/serialize/index.js new file mode 100644 index 000000000..3eedf88df --- /dev/null +++ b/lib/_utils/serialize/index.js @@ -0,0 +1,9 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.default = function (value) { + return JSON.stringify(value).replace(/\<\/script>/g, "<\\/script>"); +}; \ No newline at end of file diff --git a/lib/_utils/urlify/index.js b/lib/_utils/urlify/index.js new file mode 100644 index 000000000..e2301541f --- /dev/null +++ b/lib/_utils/urlify/index.js @@ -0,0 +1,55 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.fileExtensionRE = undefined; +exports.default = urlify; + +var _pathToUri = require("../path-to-uri"); + +var _pathToUri2 = _interopRequireDefault(_pathToUri); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var fileExtensionRE = exports.fileExtensionRE = /\.html?$/; +function urlify(string) { + var full = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + var hasExtension = string.match(fileExtensionRE); + + var url = string; + + // replace windows backslash by slash + // before playing more with the url + url = url.replace(/\\/g, "/"); + + url = url + // something-else.md => something-else + // something-else.markdown => something-else + .replace(/\.(md|markdown|txt|tex|textile|t2t|asciidoc|asc|adoc)$/, "") + + // something/index.md => something + // something/index.markdown => something + .replace(/\bindex$/, ""); + + // if url is not and html file, we will tweak it a little bit depending on the + // length wanted (full url or folder url) + if (!hasExtension) { + if (full) { + // url without extension => folder => index.html + url = (0, _pathToUri2.default)(url, "index.html"); + } else { + // url without extension => folder + if (url.length && !url.endsWith("/")) { + url += "/"; + } + } + } + // else, url with a file extension, don't touch + + // no relative url + url = url.replace(/^\.\//, ""); + + return url; +} \ No newline at end of file diff --git a/lib/bin/check-engine.js b/lib/bin/check-engine.js new file mode 100644 index 000000000..524932cad --- /dev/null +++ b/lib/bin/check-engine.js @@ -0,0 +1,43 @@ +"use strict"; + +var _child_process = require("child_process"); + +var _semver = require("semver"); + +var _semver2 = _interopRequireDefault(_semver); + +var _chalk = require("chalk"); + +var _chalk2 = _interopRequireDefault(_chalk); + +var _bins = require("../_utils/bins.js"); + +var _package = require("../../package.json"); + +var _package2 = _interopRequireDefault(_package); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +module.exports = function (nodeVersion, npmVersion, yarnVersion) { + var requirements = _package2.default.engines; + nodeVersion = nodeVersion || process.version; + + npmVersion = npmVersion || (0, _child_process.execSync)(_bins.npm + " --version").toString().trim(); + + try { + yarnVersion = yarnVersion !== undefined ? yarnVersion : (0, _child_process.execSync)(_bins.yarn + " --version").toString().trim(); + } catch (e) { + // nothing, assuming yarn does not exist + } + + if (!(_semver2.default.satisfies(nodeVersion, requirements.node) && (_semver2.default.satisfies(npmVersion, requirements.npm) || yarnVersion && _semver2.default.satisfies(yarnVersion, requirements.yarn)))) { + var errorMessage = _chalk2.default.yellow("\n⚠️ " + "Phenomic requires at least " + "node@" + requirements.node + " and " + "npm@" + requirements.npm + " (or yarn@" + requirements.yarn + ")" + "\n\n" + "Your node version is " + nodeVersion + (yarnVersion ? ", " : " and ") + "your npm version is " + npmVersion + (!yarnVersion ? "" : " and " + "your yarn version is " + yarnVersion) + "\n\n" + _chalk2.default.yellow("See 'Setup' instruction in documentation.") + " " + "https://phenomic.io/docs/setup/"); + + if (process.env.TESTING) { + throw new Error(errorMessage); + } + + console.error(errorMessage); + process.exit(1); + } +}; \ No newline at end of file diff --git a/lib/bin/commands/setup/index.js b/lib/bin/commands/setup/index.js new file mode 100644 index 000000000..c3584dbc5 --- /dev/null +++ b/lib/bin/commands/setup/index.js @@ -0,0 +1,136 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _path = require("path"); + +var _chalk = require("chalk"); + +var _chalk2 = _interopRequireDefault(_chalk); + +var _fsPromise = require("fs-promise"); + +var _fsPromise2 = _interopRequireDefault(_fsPromise); + +var _globby = require("globby"); + +var _globby2 = _interopRequireDefault(_globby); + +var _inquirer = require("inquirer"); + +var _package = require("../../../../package.json"); + +var _package2 = require("../../../../themes/phenomic-theme-base/package.json"); + +var _package3 = _interopRequireDefault(_package2); + +var _log = require("../../../_utils/log"); + +var _questions = require("./questions"); + +var _questions2 = _interopRequireDefault(_questions); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +var themePath = (0, _path.join)(__dirname, "../../../../themes/phenomic-theme-base"); + +exports.default = function () { + var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(argv) { + var cwd, testMode, answers, name, homepage, twitter, repository, phenomic, devDependencies, pkg, files; + return regeneratorRuntime.wrap(function _callee$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + cwd = process.cwd(); + testMode = argv.test; + + + (0, _log.plainLog)("Note: All values can be adjusted later."); + + _context.prev = 3; + + if (!testMode) { + _context.next = 8; + break; + } + + _context.t0 = _questions.defaultTestAnswers; + _context.next = 11; + break; + + case 8: + _context.next = 10; + return (0, _inquirer.prompt)(_questions2.default); + + case 10: + _context.t0 = _context.sent; + + case 11: + answers = _context.t0; + name = answers.name, homepage = answers.homepage, twitter = answers.twitter, repository = answers.repository, phenomic = _objectWithoutProperties(answers, ["name", "homepage", "twitter", "repository"]); + devDependencies = _extends({}, _package3.default.devDependencies, !testMode && { + phenomic: "^" + _package.version + }); + pkg = _extends({}, _package3.default, { + name: name, + homepage: homepage, + phenomic: phenomic, + twitter: twitter, + repository: repository, + devDependencies: devDependencies + }); + _context.next = 17; + return _fsPromise2.default.writeJson((0, _path.join)(cwd, "package.json"), pkg); + + case 17: + (0, _log.plainLog)("`package.json` generated"); + + files = _globby2.default.sync(["*", + // node_modules is excluded because can be present during tests + // (but will never be in public package) + "!node_modules", + // already generated + "!package.json", + // we assume it's up to the user + "!yarn.lock"], { dot: true, cwd: themePath }); + _context.next = 21; + return Promise.all(files.map(function (file) { + return _fsPromise2.default.copy((0, _path.join)(themePath, file), (0, _path.join)(cwd, file)); + })); + + case 21: + (0, _log.plainLog)("Base theme installed"); + + (0, _log.plainLog)("Project ready. Only one `npm install` and you are good to go!", "success"); + _context.next = 29; + break; + + case 25: + _context.prev = 25; + _context.t1 = _context["catch"](3); + + console.error(_chalk2.default.red(_context.t1)); + process.exit(1); + + case 29: + case "end": + return _context.stop(); + } + } + }, _callee, this, [[3, 25]]); + })); + + function setup(_x) { + return _ref.apply(this, arguments); + } + + return setup; +}(); \ No newline at end of file diff --git a/lib/bin/commands/setup/questions.js b/lib/bin/commands/setup/questions.js new file mode 100644 index 000000000..8abb54241 --- /dev/null +++ b/lib/bin/commands/setup/questions.js @@ -0,0 +1,72 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.defaultTestAnswers = undefined; + +var _validUrl = require("valid-url"); + +var _validUrl2 = _interopRequireDefault(_validUrl); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var defaultTestAnswers = exports.defaultTestAnswers = { + name: "phenomic-theme-base", + homepage: "https://phenomic.io/themes/base/demo", + twitter: "Phenomic_app", + repository: "https://github.com/MoOx/phenomic", + CNAME: false +}; + +var packageJsonNameRE = /^[a-zA-Z0-9\-]+$/; +var twitterRE = /^[a-zA-Z0-9\-_]+$/; + +var questions = [{ + type: "input", + name: "name", + message: "Dashed name of your project (eg: my-project)", + validate: function validate(value) { + if (packageJsonNameRE.test(value)) { + return true; + } + return "Only letters, numbers and dashes are allowed."; + } +}, { + type: "input", + name: "homepage", + message: "Website url (eg: http://abc.xyz/)", + validate: function validate(value) { + if (_validUrl2.default.isWebUri(value)) { + return true; + } + return "Please provide a valid url"; + } +}, { + type: "input", + name: "repository", + message: "Repository url" + " (eg: https://github.com/MoOx/phenomic.git, optional)", + validate: function validate(value) { + if (value === "" || _validUrl2.default.isWebUri(value)) { + return true; + } + return "Please provide a valid url for repository"; + } +}, { + type: "input", + name: "twitter", + message: "Twitter nickname (eg: MoOx, optional)", + validate: function validate(value) { + if (value === "" || twitterRE.test(value)) { + return true; + } + return "Only letters, numbers, dashes & underscores are allowed."; + } +}, { + type: "confirm", + name: "CNAME", + message: "Do you want a CNAME file (eg: for GitHub Pages)?", + default: false +}]; + +exports.default = questions; \ No newline at end of file diff --git a/lib/bin/index.js b/lib/bin/index.js new file mode 100755 index 000000000..df1be1040 --- /dev/null +++ b/lib/bin/index.js @@ -0,0 +1,11 @@ +#!/usr/bin/env node +"use strict"; + +require("babel-register")(); + +// Check for node and npm version +// If it doesn't sastify the requirements +// The process will exits immediately +require("./check-engine")(); + +require("./phenomic.js"); \ No newline at end of file diff --git a/lib/bin/phenomic.js b/lib/bin/phenomic.js new file mode 100644 index 000000000..205033d5d --- /dev/null +++ b/lib/bin/phenomic.js @@ -0,0 +1,75 @@ +"use strict"; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +require("babel-polyfill"); + +var _path = require("path"); + +var _yargs = require("../configurator/yargs.js"); + +var _yargs2 = _interopRequireDefault(_yargs); + +var _index = require("../configurator/index.js"); + +var _index2 = _interopRequireDefault(_index); + +var _log = require("../_utils/log"); + +var _log2 = _interopRequireDefault(_log); + +var _index3 = require("./commands/setup/index.js"); + +var _index4 = _interopRequireDefault(_index3); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var runner = function runner() { + (0, _log2.default)("Phenomic is starting", "info"); + var cwd = process.cwd(); + var pkg = require((0, _path.join)(cwd, "package.json")); + var config = (0, _index2.default)({ argv: process.argv, pkg: pkg }); + + // lazyload builder to avoid issue when webpack is no installed yet + // @todo move runner() out of this file so setup can work without + // lazyloading via "require()" + var builder = require("../builder/index.js").default; + builder(config); +}; + +var startAndBuildOptions = { + "webpack-config": { + type: "string", + describe: "Webpack config (must export a function)", + default: "webpack.config.js" + }, + "script-browser": { + type: "string", + describe: "Phenomic entry point (browser)", + default: (0, _path.join)("scripts", "phenomic.browser.js") + }, + "script-node": { + type: "string", + describe: "Phenomic entry point (node)", + default: (0, _path.join)("scripts", "phenomic.node.js") + } +}; + +_yargs2.default.command("setup", "setup a project", { + test: { + describe: "Test mode (don't use this option)." + } +}, _index4.default); + +_yargs2.default.command("start", "start your project (server / development mode)", _extends({}, startAndBuildOptions, { + dev: { default: true }, + server: { default: true }, + open: { default: true } +}), runner); + +_yargs2.default.command("build", "build your project (static / production mode)", _extends({}, startAndBuildOptions, { + production: { default: true }, + static: { default: true } +}), runner); + +_yargs2.default.parse(process.argv); \ No newline at end of file diff --git a/lib/builder/dynamic-require.js b/lib/builder/dynamic-require.js new file mode 100644 index 000000000..5709add0e --- /dev/null +++ b/lib/builder/dynamic-require.js @@ -0,0 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +// @ flow +// flow does not like require(var), so I am just doing this ugly trick to skip +// flow warning. + +exports.default = require; \ No newline at end of file diff --git a/lib/builder/index.js b/lib/builder/index.js new file mode 100644 index 000000000..4e4596e25 --- /dev/null +++ b/lib/builder/index.js @@ -0,0 +1,147 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +// module.exports is used +// eslint-disable-next-line import/default + + +exports.default = function (config) { + // log(JSON.stringify(config, null, 2)) + + var makeWebpackConfigModule = (0, _dynamicRequire2.default)((0, _path.join)(config.cwd, config["webpackConfig"])); + var makeWebpackConfig = typeof makeWebpackConfigModule.default === "function" ? makeWebpackConfigModule.default + // : makeWebpackConfigModule + // @todo remove block below and uncomment line above + : typeof makeWebpackConfigModule === "function" ? makeWebpackConfigModule + // deprecated + : makeWebpackConfigModule.makeConfig; + + // deprecated + if (makeWebpackConfigModule.makeConfig) { + (0, _log2.default)("Your webpack config should now directly export a function.\n" + "No need to export a makeConfig() function anymore as webpack@2 " + "natively support a function. " + "(makeConfig() is currently deprecated and support will be remove in a " + "futur release). ", "warning"); + } + + if (typeof makeWebpackConfig !== "function") { + throw new Error("Your webpack config must export a function. " + "This function will be called with a single argument " + "(Phenomic configuration object) and must return a valid webpack config."); + } + + config.webpackConfig = makeWebpackConfig(config); + config.webpackConfigBrowser = (0, _configBrowser2.default)(config); + config.webpackConfigNode = (0, _configNode2.default)(config); + + var destination = (0, _path.join)(config.cwd, config.destination); + _fsExtra2.default.emptyDirSync(destination); + + process.env.BABEL_ENV = "webpack-" + (process.env.NODE_ENV || "development"); + + if (config.static) { + // Copy static assets to build folder + if (config.assets) { + (0, _log2.default)("Copying assets", function () { + var copyDest = (0, _path.join)(destination, config.assets.route); + _fsExtra2.default.copySync(config.assets.path, copyDest); + }); + } + + var webpackPromise = function webpackPromise(webpackConfig) { + return new Promise(function (resolve, reject) { + try { + (0, _webpack2.default)(webpackConfig, _log2.default, resolve); + } catch (e) { + reject(e); + } + }); + }; + + (0, _log2.default)("Building client files", webpackPromise(config.webpackConfigBrowser)).then(function (clientBundleStats) { + // Sometimes, webpack does not throw an error, but send it in the + // callback... PRETTY WEIRD RIGHT? + // https://github.com/webpack/webpack/issues/2217#issuecomment-249364851 + if (clientBundleStats instanceof Error) { + throw clientBundleStats; + } + return (0, _log2.default)("Preparing static build", webpackPromise(config.webpackConfigNode)).then(function () { + return clientBundleStats; + }); + }).then(function (clientBundleStats) { + return (0, _log2.default)("Building static files", function () { + var staticBuild = (0, _dynamicRequire2.default)((0, _path.join)(config.webpackConfigNode.output.path, config.webpackConfigNode.output.filename)); + // transpilation shit + // https://github.com/webpack/webpack/issues/4039 + return (typeof staticBuild.default === "function" ? staticBuild.default : staticBuild)(_extends({}, config, { + collection: _plugin2.default.collection, + assetsFiles: (0, _sortAssets2.default)(clientBundleStats.toJson().assetsByChunkName) + })); + }); + }).then(function (files) { + return (0, _postBuild2.default)(config, files); + }).then(function () { + return config.server && (0, _server2.default)(config); + }).catch(function (err) { + (0, _log2.default)(_chalk2.default.red("Build failed"), "error"); + setTimeout(function () { + throw (0, _errorFormatter2.default)(err); + }, 1); + }); + } else if (config.server) { + (0, _server2.default)(config); + } else { + throw new Error(_chalk2.default.red("phenomic: CLI needs --static or --server")); + } +}; + +var _path = require("path"); + +var _fsExtra = require("fs-extra"); + +var _fsExtra2 = _interopRequireDefault(_fsExtra); + +var _chalk = require("chalk"); + +var _chalk2 = _interopRequireDefault(_chalk); + +var _log = require("../_utils/log"); + +var _log2 = _interopRequireDefault(_log); + +var _errorFormatter = require("../_utils/error-formatter"); + +var _errorFormatter2 = _interopRequireDefault(_errorFormatter); + +var _plugin = require("../loader/plugin.js"); + +var _plugin2 = _interopRequireDefault(_plugin); + +var _webpack = require("./webpack"); + +var _webpack2 = _interopRequireDefault(_webpack); + +var _sortAssets = require("./webpack/sortAssets.js"); + +var _sortAssets2 = _interopRequireDefault(_sortAssets); + +var _server = require("./server.js"); + +var _server2 = _interopRequireDefault(_server); + +var _postBuild = require("./post-build.js"); + +var _postBuild2 = _interopRequireDefault(_postBuild); + +var _configBrowser = require("./webpack/config.browser.js"); + +var _configBrowser2 = _interopRequireDefault(_configBrowser); + +var _configNode = require("./webpack/config.node.js"); + +var _configNode2 = _interopRequireDefault(_configNode); + +var _dynamicRequire = require("./dynamic-require.js"); + +var _dynamicRequire2 = _interopRequireDefault(_dynamicRequire); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } \ No newline at end of file diff --git a/lib/builder/openChrome.applescript b/lib/builder/openChrome.applescript new file mode 100644 index 000000000..64b1033be --- /dev/null +++ b/lib/builder/openChrome.applescript @@ -0,0 +1,45 @@ +(* +Ripped from https://gist.github.com/mayoff/1138816 +*) + +on run argv + set theURL to item 1 of argv + + tell application "Chrome" + + if (count every window) = 0 then + make new window + end if + + -- Find a tab currently running the debugger + set found to false + set theTabIndex to -1 + repeat with theWindow in every window + set theTabIndex to 0 + repeat with theTab in every tab of theWindow + set theTabIndex to theTabIndex + 1 + # found url from the same path, not only exact urls + # if theTab's URL is theURL then + if theTab's URL contains theURL then + set found to true + exit repeat + end if + end repeat + + if found then + exit repeat + end if + end repeat + + if found then + tell theTab to reload + set index of theWindow to 1 + set theWindow's active tab index to theTabIndex + else + tell window 1 + activate + make new tab with properties {URL:theURL} + end tell + end if + end tell +end run diff --git a/lib/builder/post-build.js b/lib/builder/post-build.js new file mode 100644 index 000000000..c1b718484 --- /dev/null +++ b/lib/builder/post-build.js @@ -0,0 +1,50 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.default = function (config, files) { + (0, _log2.default)(files.length + " files written", "info"); + + var promises = []; + + if (config.CNAME) { + promises.push(writeFile((0, _path.join)(config.cwd, config.destination, "CNAME"), config.baseUrl.hostname).then(function () { + return (0, _log2.default)("CNAME created with '" + config.baseUrl.hostname + "'"); + })); + } + + if (config.nojekyll) { + promises.push(writeFile((0, _path.join)(config.cwd, config.destination, ".nojekyll"), "").then(function () { + return (0, _log2.default)(".nojekyll created"); + })); + } + + return Promise.all(promises).then(function () { + return (0, _log.plainLog)(_chalk2.default.green("Build successful") + " " + (0, _log.totalElapsedTime)()); + }); +}; + +var _path = require("path"); + +var _fs = require("fs"); + +var _fs2 = _interopRequireDefault(_fs); + +var _pify2 = require("pify"); + +var _pify3 = _interopRequireDefault(_pify2); + +var _chalk = require("chalk"); + +var _chalk2 = _interopRequireDefault(_chalk); + +var _log = require("../_utils/log"); + +var _log2 = _interopRequireDefault(_log); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var _pify = (0, _pify3.default)(_fs2.default), + writeFile = _pify.writeFile; \ No newline at end of file diff --git a/lib/builder/server.js b/lib/builder/server.js new file mode 100644 index 000000000..b59cfeb79 --- /dev/null +++ b/lib/builder/server.js @@ -0,0 +1,204 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +// module.exports is used +// eslint-disable-next-line import/default + + +var _path = require("path"); + +var _child_process = require("child_process"); + +var _express = require("express"); + +var _express2 = _interopRequireDefault(_express); + +var _webpack = require("webpack"); + +var _webpack2 = _interopRequireDefault(_webpack); + +var _webpackDevMiddleware = require("webpack-dev-middleware"); + +var _webpackDevMiddleware2 = _interopRequireDefault(_webpackDevMiddleware); + +var _webpackHotMiddleware = require("webpack-hot-middleware"); + +var _webpackHotMiddleware2 = _interopRequireDefault(_webpackHotMiddleware); + +var _connectHistoryApiFallback = require("connect-history-api-fallback"); + +var _connectHistoryApiFallback2 = _interopRequireDefault(_connectHistoryApiFallback); + +var _portfinder = require("portfinder"); + +var _portfinder2 = _interopRequireDefault(_portfinder); + +var _opn = require("opn"); + +var _opn2 = _interopRequireDefault(_opn); + +var _log = require("../_utils/log"); + +var _log2 = _interopRequireDefault(_log); + +var _plugin = require("../loader/plugin.js"); + +var _plugin2 = _interopRequireDefault(_plugin); + +var _minify = require("../loader/minify"); + +var _minify2 = _interopRequireDefault(_minify); + +var _serialize = require("../_utils/serialize"); + +var _serialize2 = _interopRequireDefault(_serialize); + +var _pathToUri = require("../_utils/path-to-uri"); + +var _pathToUri2 = _interopRequireDefault(_pathToUri); + +var _logFormatter = require("./webpack/log-formatter.js"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +var devServerNoScript = "Phenomic development server requires JavaScript.\nIf you want to check our your website works without JavaScript, you need to\nbuild the static version and server the result.\nYou can do this by doing: npm run build -- --serve\n"; + +exports.default = function (config) { + var webpackConfig = config.webpackConfigBrowser; + + + if (!config.baseUrl) { + throw new Error("You must provide a 'baseUrl' object that contains the following keys:" + "'href', 'port', 'hostname'. See https://nodejs.org/api/url.html"); + } + + var server = (0, _express2.default)(); + + if (config.static && config.server) { + server.use(config.baseUrl.pathname, _express2.default.static((0, _path.join)(config.cwd, config.destination))); + } else { + var devEntries = [require.resolve("webpack-hot-middleware/client")]; + + var devConfig = _extends({}, webpackConfig, { + entry: _extends({}, Object.keys(webpackConfig.entry).reduce(function (acc, key) { + return _extends({}, acc, _defineProperty({}, key, [].concat(devEntries, _toConsumableArray(Array.isArray(webpackConfig.entry[key]) ? webpackConfig.entry[key] : [webpackConfig.entry[key]])))); + }, {})), + plugins: [].concat(_toConsumableArray(webpackConfig.plugins || []), [ + + // for hot-middleware + new _webpack2.default.optimize.OccurrenceOrderPlugin(), new _webpack2.default.HotModuleReplacementPlugin(), new _webpack2.default.NoEmitOnErrorsPlugin()]) + }); + + // webpack dev + hot middlewares + var webpackCompiler = (0, _webpack2.default)(devConfig); + + server.use((0, _webpackDevMiddleware2.default)(webpackCompiler, _extends({ + publicPath: webpackConfig.output.publicPath, + // skip compilation logs if !verbose + noInfo: !config.verbose, + quiet: !config.verbose + }, devConfig.devServer))); + server.use((0, _webpackHotMiddleware2.default)(webpackCompiler, { + // skip hot middleware logs if !verbose + log: config.verbose ? undefined : function () {} + })); + + var entries = []; + webpackCompiler.plugin("done", function (stats) { + entries = []; + var namedChunks = stats.compilation.namedChunks; + Object.keys(namedChunks).forEach(function (chunkName) { + entries = [].concat(_toConsumableArray(entries), _toConsumableArray(namedChunks[chunkName].files.filter(function (file) { + return !file.endsWith(".hot-update.js"); + }))); + }); + }); + + // if !verbose, use our custom minimal output + if (!config.verbose) { + (0, _logFormatter.handleInvalid)(); // start "Updating" + webpackCompiler.plugin("invalid", _logFormatter.handleInvalid); + webpackCompiler.plugin("done", _logFormatter.handleDone); + } + + // user static assets + if (config.assets) { + server.use(config.baseUrl.pathname + config.assets.route, _express2.default.static(config.assets.path)); + } + + // routing for the part we want (starting to the baseUrl pathname) + var router = (0, _express.Router)(); + server.use(config.baseUrl.pathname, router); + + // fallback to index for unknow pages? + router.use((0, _connectHistoryApiFallback2.default)({ + // https://github.com/MoOx/phenomic/issues/808 + disableDotRule: true + })); + + // webpack static ressources + router.get("*", _express2.default.static(webpackConfig.output.path)); + + // hardcoded entry point + router.get("/index.html", function (req, res) { + var collectionMin = (0, _minify2.default)(_plugin2.default.collection); + res.setHeader("Content-Type", "text/html"); + /* eslint-disable max-len */ + res.end("\n \n \n \n
\n \n \n \n
\n \n \n " + entries.map(function (fileName) { + return ""; + }) + "\n \n " + /* eslint-enable max-len */ + ); + }); + } + + // THAT'S IT + var devHost = config.devHost, + devPort = config.devPort; + + + _portfinder2.default.basePort = devPort; + + _portfinder2.default.getPort(function (err, port) { + if (err) { + throw err; + } + + if (port !== devPort) { + (0, _log2.default)("Port " + devPort + " is not available. Using port " + port + " instead."); + } + + server.listen(port, devHost, function (err) { + if (err) { + throw err; + } + var href = "http://" + devHost + ":" + port + config.baseUrl.pathname; + (0, _log2.default)("Development server listening on " + href); + + if (config.open) { + var openUrl = href.replace(devHost, "localhost"); + if (process.platform === "darwin") { + try { + // Try our best to reuse existing tab + // on OS X Google Chrome with AppleScript + (0, _child_process.execSync)("ps cax | grep \"Google Chrome\""); + (0, _child_process.execSync)("osascript openChrome.applescript " + openUrl, { cwd: __dirname, stdio: "ignore" }); + return true; + } catch (err) { + // Ignore errors. + } + } + // Fallback to opn + // (It will always open new tab) + (0, _opn2.default)(openUrl); + } + }); + }); +}; \ No newline at end of file diff --git a/lib/builder/webpack/config.browser.js b/lib/builder/webpack/config.browser.js new file mode 100644 index 000000000..03319094c --- /dev/null +++ b/lib/builder/webpack/config.browser.js @@ -0,0 +1,41 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +// module.exports is used +// eslint-disable-next-line import/default + + +var _path = require("path"); + +var _webpack = require("../../_utils/offline/webpack.js"); + +var _plugin = require("../../loader/plugin.js"); + +var _plugin2 = _interopRequireDefault(_plugin); + +var _configCommon = require("./config.common.js"); + +var _configCommon2 = _interopRequireDefault(_configCommon); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +var chunkNameBrowser = "phenomic.browser"; + +exports.default = function (config) { + + var webpackConfig = (0, _configCommon2.default)(config); + + return _extends({}, webpackConfig, { + plugins: [new _plugin2.default()].concat(_toConsumableArray(webpackConfig.plugins), _toConsumableArray((0, _webpack.offlinePlugin)(config))), + + entry: _extends({}, config.webpackConfig ? config.webpackConfig.entry : {}, _defineProperty({}, chunkNameBrowser, [(0, _path.join)(config.cwd, config.scriptBrowser)].concat(_toConsumableArray((0, _webpack.offlineEntry)(config))))) + }); +}; \ No newline at end of file diff --git a/lib/builder/webpack/config.common.js b/lib/builder/webpack/config.common.js new file mode 100644 index 000000000..83e0d5f8a --- /dev/null +++ b/lib/builder/webpack/config.common.js @@ -0,0 +1,49 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.chunkNameBrowser = undefined; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _url = require("url"); + +var _url2 = _interopRequireDefault(_url); + +var _webpack = require("webpack"); + +var _package = require("../../../package.json"); + +var _package2 = _interopRequireDefault(_package); + +var _webpack2 = require("../../_utils/cache/webpack.js"); + +var _webpack3 = _interopRequireDefault(_webpack2); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +var chunkNameBrowser = exports.chunkNameBrowser = "phenomic.browser"; + +var wrap = JSON.stringify; + +exports.default = function (config) { + var _config$webpackConfig = config.webpackConfig, + webpackConfig = _config$webpackConfig === undefined ? {} : _config$webpackConfig; + + + return _extends({}, webpackConfig, { + plugins: [].concat(_toConsumableArray(webpackConfig.plugins ? webpackConfig.plugins : []), _toConsumableArray((0, _webpack3.default)(config)), [new _webpack.DefinePlugin({ "process.env": { + NODE_ENV: wrap(config.production ? "production" : process.env.NODE_ENV), + + PHENOMIC_USER_PATHNAME: wrap(process.env.PHENOMIC_USER_PATHNAME), + PHENOMIC_USER_URL: wrap(_url2.default.format(config.baseUrl)), + PHENOMIC_NAME: wrap(_package2.default.name[0].toUpperCase() + _package2.default.name.slice(1)), + PHENOMIC_VERSION: wrap(_package2.default.version), + PHENOMIC_HOMEPAGE: wrap(_package2.default.homepage), + PHENOMIC_REPOSITORY: wrap(_package2.default.repository) + } })]) + }); +}; \ No newline at end of file diff --git a/lib/builder/webpack/config.node.js b/lib/builder/webpack/config.node.js new file mode 100644 index 000000000..e97d20f16 --- /dev/null +++ b/lib/builder/webpack/config.node.js @@ -0,0 +1,84 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.cacheDir = undefined; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _path = require("path"); + +var _webpack = require("webpack"); + +var _findCacheDir = require("find-cache-dir"); + +var _findCacheDir2 = _interopRequireDefault(_findCacheDir); + +var _configCommon = require("./config.common.js"); + +var _configCommon2 = _interopRequireDefault(_configCommon); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +var UglifyJsPlugin = _webpack.optimize.UglifyJsPlugin; + +var chunkNameNode = "phenomic.node"; +var cacheDir = exports.cacheDir = (0, _findCacheDir2.default)({ name: "phenomic/webpack-node-build" }); + +var defaultExternals = [ +// we could consider node_modules as externals deps +// and so use something like +// /^[A-Za-z0-9-_]/ +// to not bundle all deps in the static build (for perf) +// the problem is that if people rely on node_modules for stuff +// like css, this breaks their build. + +// Glamor integration +"glamor", "glamor/server", + +// Aprodite integration +"aphrodite"]; + +var sourceMapSupport = require.resolve("source-map-support/register") +// windows support +.replace(/\\/g, "/"); + +var requireSourceMapSupport = "require('" + sourceMapSupport + "');"; + +exports.default = function (config) { + var webpackConfig = (0, _configCommon2.default)(config); + + return _extends({}, webpackConfig, { + + entry: _defineProperty({}, chunkNameNode, (0, _path.join)(config.cwd, config.scriptNode)), + + output: _extends({}, webpackConfig.output, { + path: cacheDir, + libraryTarget: "commonjs2", + filename: (0, _path.basename)(config.scriptNode, ".js") + ".bundle.js" + }), + + target: "node", + + // externals for package/relative name + externals: [].concat(_toConsumableArray(webpackConfig.externals || defaultExternals), [ + + // keep the loader plugin cache in memory + "phenomic/lib/loader/index", "phenomic/lib/loader/plugin"]), + + // sourcemaps + devtool: "#source-map", + plugins: [].concat(_toConsumableArray(webpackConfig.plugins.filter(function (plugin) { + return !(plugin instanceof UglifyJsPlugin); + }) || []), [new _webpack.BannerPlugin({ + banner: requireSourceMapSupport, + raw: true, + entryOnly: false + })]) + }); +}; \ No newline at end of file diff --git a/lib/builder/webpack/index.js b/lib/builder/webpack/index.js new file mode 100644 index 000000000..f6768f075 --- /dev/null +++ b/lib/builder/webpack/index.js @@ -0,0 +1,38 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.default = function (webpackConfig, log, cb) { + (0, _webpack2.default)(webpackConfig, function (err, stats) { + if (err) { + throw err; + } + + if (stats.hasErrors()) { + stats.compilation.errors.forEach(function (item) { + return log(_chalk2.default.red(item.stack || item)); + }); + throw new Error("webpack build failed with errors"); + } + + if (stats.hasWarnings()) { + stats.compilation.warnings.forEach(function (item) { + return log(_chalk2.default.yellow("Warning: %s", item.message)); + }); + } + + cb(stats); + }); +}; + +var _webpack = require("webpack"); + +var _webpack2 = _interopRequireDefault(_webpack); + +var _chalk = require("chalk"); + +var _chalk2 = _interopRequireDefault(_chalk); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } \ No newline at end of file diff --git a/lib/builder/webpack/log-formatter.js b/lib/builder/webpack/log-formatter.js new file mode 100644 index 000000000..a8b05d370 --- /dev/null +++ b/lib/builder/webpack/log-formatter.js @@ -0,0 +1,76 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.handleInvalid = handleInvalid; +exports.handleDone = handleDone; + +var _chalk = require("chalk"); + +var _chalk2 = _interopRequireDefault(_chalk); + +var _ora = require("ora"); + +var _ora2 = _interopRequireDefault(_ora); + +var _logSymbols = require("log-symbols"); + +var _logSymbols2 = _interopRequireDefault(_logSymbols); + +var _log = require("../../_utils/log"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var spinner = (0, _ora2.default)(); + +// remove noize from messages +var betterMsg = function betterMsg(msg) { + return msg.replace(process.cwd(), ".") + + // Webpack "Module not found" noize + .replace(/Error: Cannot resolve 'file' or 'directory'/, "") + + // loader path for css + .replace(/.\/~\/(css|postcss|sass|less|stylus)-loader(.*)!/g, ""); +}; + +function handleInvalid() { + spinner.text = "Updating"; + spinner.start(); +} + +var isSyntaxError = function isSyntaxError(msg) { + return msg.includes("SyntaxError"); +}; + +function handleDone(webpackStats) { + spinner.stop(); + + var stats = webpackStats.toJson({}, true); + if (!webpackStats.hasErrors() && !webpackStats.hasWarnings()) { + spinner.stream.write(_logSymbols2.default.success + _chalk2.default.green(" Updated ") + ("(in " + (0, _log.formatTime)(stats.time / 1000) + ")")); + return; + } + + if (stats.errors.length) { + spinner.text = _chalk2.default.red("Update failed") + "\n"; + spinner.fail(); + spinner = (0, _ora2.default)(); + + var errors = stats.errors.some(isSyntaxError) + // Show syntax error first + ? stats.errors.filter(isSyntaxError) : stats.errors; + errors.forEach(function (msg) { + return (0, _log.plainLog)("Error in " + betterMsg(msg) + "\n", "error"); + }); + return; + } + + spinner.text = _chalk2.default.yellow("Updated with warnings") + "\n"; + spinner.fail(); + spinner = (0, _ora2.default)(); + stats.warnings.forEach(function (msg) { + return (0, _log.plainLog)("Warning in " + betterMsg(msg) + " \n", "warning"); + }); +} \ No newline at end of file diff --git a/lib/builder/webpack/sortAssets.js b/lib/builder/webpack/sortAssets.js new file mode 100644 index 000000000..3cb0bd8f8 --- /dev/null +++ b/lib/builder/webpack/sortAssets.js @@ -0,0 +1,24 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.default = function (assets) { + var assetsFiles = { css: [], js: [] }; + + Object.keys(assets).reduce(function (result, key) { + var chunkAssets = assets[key]; + return result.concat(chunkAssets); + }, []).sort(function (a, b) { + return a.toLowerCase() > b.toLowerCase() ? 1 : -1; + }).forEach(function (name) { + if (name.endsWith(".js")) { + assetsFiles.js.push(name); + } else if (name.endsWith(".css")) { + assetsFiles.css.push(name); + } + }); + + return assetsFiles; +}; \ No newline at end of file diff --git a/lib/client/hot-md.js b/lib/client/hot-md.js new file mode 100644 index 000000000..9f4346b5c --- /dev/null +++ b/lib/client/hot-md.js @@ -0,0 +1,25 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _pages = require("../redux/modules/pages"); + +var pageActions = _interopRequireWildcard(_pages); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +exports.default = function (mdContext, collection, store) { + return function (file) { + var item = collection.find(function (item) { + return item.__filename === file.slice("./".length); + }); + var dataUrl = mdContext(file); + if (dataUrl !== item.__dataUrl) { + item.__dataUrl = dataUrl; + console.log(file, " hot update"); + store.dispatch(pageActions.refresh(item.__url, item.__dataUrl)); + } + }; +}; // eslint-disable-next-line import/no-namespace \ No newline at end of file diff --git a/lib/client/index.js b/lib/client/index.js new file mode 100644 index 000000000..1321b7272 --- /dev/null +++ b/lib/client/index.js @@ -0,0 +1,70 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.browserHistory = undefined; +exports.default = phenomic; + +var _react = require("react"); + +var _react2 = _interopRequireDefault(_react); + +var _reactDom = require("react-dom"); + +var _reactDom2 = _interopRequireDefault(_reactDom); + +var _reactRouter = require("react-router"); + +var _createBrowserHistory = require("history/lib/createBrowserHistory"); + +var _createBrowserHistory2 = _interopRequireDefault(_createBrowserHistory); + +var _useScroll = require("react-router-scroll/lib/useScroll"); + +var _useScroll2 = _interopRequireDefault(_useScroll); + +var _reactRedux = require("react-redux"); + +var _ContextProvider = require("../components/ContextProvider"); + +var _ContextProvider2 = _interopRequireDefault(_ContextProvider); + +var _shouldUpdateScroll = require("./should-update-scroll.js"); + +var _shouldUpdateScroll2 = _interopRequireDefault(_shouldUpdateScroll); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// App +var browserHistory = exports.browserHistory = typeof window !== "undefined" // just for node testing +? (0, _reactRouter.useRouterHistory)(_createBrowserHistory2.default)({ + // basename don't like having a trailing slash + // https://github.com/reactjs/react-router/issues/3184 + basename: process.env.PHENOMIC_USER_PATHNAME.replace(/\/$/, "") +}) : null; + +function phenomic(_ref) { + var metadata = _ref.metadata, + routes = _ref.routes, + store = _ref.store; + + var collection = typeof window !== "undefined" ? window.__COLLECTION__ : []; + + _reactDom2.default.render(_react2.default.createElement( + _ContextProvider2.default, + { + collection: collection, + metadata: metadata + }, + _react2.default.createElement( + _reactRedux.Provider, + { store: store }, + _react2.default.createElement(_reactRouter.Router, { + history: browserHistory, + routes: routes, + render: (0, _reactRouter.applyRouterMiddleware)((0, _useScroll2.default)(_shouldUpdateScroll2.default)) + }) + ) + ), document.getElementById("phenomic")); +} \ No newline at end of file diff --git a/lib/client/should-update-scroll.js b/lib/client/should-update-scroll.js new file mode 100644 index 000000000..65faed75e --- /dev/null +++ b/lib/client/should-update-scroll.js @@ -0,0 +1,83 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + + +var lookForElement = function lookForElement(id) { + var element = document.getElementById(id); + if (element === null) { + var elementsSet = document.getElementsByName(id); + if (elementsSet.length) { + element = elementsSet[0]; + } + } + + return element; +}; + +var retryTimeout = null; +var retryCount = 0; +var retryDelay = 100; +var retryMax = 1000; // 1000 * retryDelay = retry during 10s + +var moveToHash = function moveToHash(id) { + var element = lookForElement(id); + if (element) { + if (element.scrollIntoViewA) { + element.scrollIntoView(); + } else { + var coords = element.getBoundingClientRect(); + // $FlowFixMe window + window.scrollTo( + // $FlowFixMe window + coords.left + window.pageXOffset, + // $FlowFixMe window + coords.top + window.pageYOffset); + } + return true; + } + if (retryCount < retryMax) { + retryTimeout = setTimeout(function () { + retryCount++; + moveToHash(id); + }, retryDelay); + } + + return false; +}; + +// only scroll when complete url changes +// (except for hash change - this example provide a natural feeling when you +// use simple link to internal anchor (eg: a table of content that use hashes +// inside a page)) + +var shouldUpdateScroll = function shouldUpdateScroll(prevProps, props) { + var hash = props.location.hash; + + // if there is a page + hash, + // it's probably not already in the page since we load page data + // asynchronously, AFTER react-router does his job + // so here we will handle the work ourselves + // (and tell react-router-scroll to not handle it) + if ( + // if page is the same, browser will handle the scroll automatically + // so we execute our logic for the scroll only if location change + // and include a hash + prevProps && prevProps.location.pathname + prevProps.location.search !== props.location.pathname + props.location.search && hash.length > 1) { + retryCount = 1; + clearTimeout(retryTimeout); + var hasScrolled = moveToHash(hash.slice(1)); + + // scroll to top until dom is ready, + // code above might handle the scroll LATER + // if it does we know it and tell react-router to scroll to top until dom + // is ready to avoid weird UX (eg: click, no scroll at all) + return !hasScrolled; + } + + return Boolean(prevProps) && hash === ""; +}; + +exports.default = shouldUpdateScroll; \ No newline at end of file diff --git a/lib/components/BodyContainer/index.js b/lib/components/BodyContainer/index.js new file mode 100644 index 000000000..f74596b88 --- /dev/null +++ b/lib/components/BodyContainer/index.js @@ -0,0 +1,83 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _react = require("react"); + +var _react2 = _interopRequireDefault(_react); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var BodyContainer = function (_Component) { + _inherits(BodyContainer, _Component); + + function BodyContainer() { + _classCallCheck(this, BodyContainer); + + return _possibleConstructorReturn(this, (BodyContainer.__proto__ || Object.getPrototypeOf(BodyContainer)).apply(this, arguments)); + } + + _createClass(BodyContainer, [{ + key: "render", + value: function render() { + var props = this.props; + + var children = props.children, + otherProps = _objectWithoutProperties(props, ["children"]); + + var child = void 0; + if (typeof children === "string") { + child = children; + } else { + try { + child = _react2.default.Children.only(children); + } catch (e) { + console.log("phenomic: BodyContainer: multiple childs"); + } + } + + if (child) { + return _react2.default.createElement("div", _extends({ + className: "phenomic-BodyContainer", + dangerouslySetInnerHTML: { __html: child } + }, otherProps)); + } + + return _react2.default.createElement( + "div", + otherProps, + _react2.default.Children.map(children, function (child, i) { + if (typeof child === "string") { + return _react2.default.createElement("div", { + key: i, + className: "phenomic-BodyContainer", + dangerouslySetInnerHTML: { __html: child } + }); + } + return child; + }) + ); + } + }]); + + return BodyContainer; +}(_react.Component); + +BodyContainer.propTypes = { + children: require("react").PropTypes.any.isRequired +}; +exports.default = BodyContainer; \ No newline at end of file diff --git a/lib/components/ContextProvider/index.js b/lib/components/ContextProvider/index.js new file mode 100644 index 000000000..d6bc8b415 --- /dev/null +++ b/lib/components/ContextProvider/index.js @@ -0,0 +1,58 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _react = require("react"); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var PhenomicContext = function (_Component) { + _inherits(PhenomicContext, _Component); + + function PhenomicContext() { + _classCallCheck(this, PhenomicContext); + + return _possibleConstructorReturn(this, (PhenomicContext.__proto__ || Object.getPrototypeOf(PhenomicContext)).apply(this, arguments)); + } + + _createClass(PhenomicContext, [{ + key: "getChildContext", + value: function getChildContext() { + return { + collection: this.props.collection, + metadata: this.props.metadata + }; + } + }, { + key: "render", + value: function render() { + return _react.Children.only(this.props.children); + } + }]); + + return PhenomicContext; +}(_react.Component); + +PhenomicContext.propTypes = { + collection: _react.PropTypes.array, + metadata: _react.PropTypes.object, + children: _react.PropTypes.node +}; +PhenomicContext.childContextTypes = { + collection: _react.PropTypes.array, + metadata: _react.PropTypes.object +}; +PhenomicContext.propTypes = { + collection: require("react").PropTypes.any.isRequired, + metadata: require("react").PropTypes.object.isRequired, + children: require("react").PropTypes.any +}; +exports.default = PhenomicContext; \ No newline at end of file diff --git a/lib/components/Html/index.js b/lib/components/Html/index.js new file mode 100644 index 000000000..c42d7ee20 --- /dev/null +++ b/lib/components/Html/index.js @@ -0,0 +1,120 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _react = require("react"); + +var _react2 = _interopRequireDefault(_react); + +var _server = require("react-dom/server"); + +var _reactHelmet = require("react-helmet"); + +var _reactHelmet2 = _interopRequireDefault(_reactHelmet); + +var _styledComponents = require("styled-components"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var Html = function Html(props) { + + // Glamor integration + // https://github.com/threepointone/glamor/blob/master/docs/server.md + var glamorRenderStatic = void 0; + try { + // $FlowFixMe just ignore glamor as we don't have it as a dep + glamorRenderStatic = require("glamor/server").renderStatic; + } catch (e) {} + // skip glamor if not working + + + // Aprodite + // https://github.com/Khan/aphrodite#server-side-rendering + var aproditeRenderStatic = void 0; + try { + // $FlowFixMe just ignore aprodite as we don't have it as a dep + aproditeRenderStatic = require("aphrodite").StyleSheetServer.renderStatic; + } catch (e) {} + // skip aprodite if not working + + + // render body + var body = void 0; + if (glamorRenderStatic) { + var glamorResult = glamorRenderStatic(function () { + return props.renderBody(); + }); + + (0, _server.renderToString)(_react2.default.createElement(_reactHelmet2.default, { + style: [{ "cssText": glamorResult.css }], + script: [{ "innerHTML": "window._glamor = " + JSON.stringify(glamorResult.ids) }] + })); + body = glamorResult.html; + } else if (aproditeRenderStatic) { + var aproditeResult = aproditeRenderStatic(function () { + return props.renderBody(); + }); + + (0, _server.renderToString)(_react2.default.createElement(_reactHelmet2.default, { + style: [{ + "cssText": aproditeResult.css.content, + "data-aphrodite": undefined + }], + script: [{ "innerHTML": "window._aphrodite = " + JSON.stringify(aproditeResult.css.renderedClassNames) + ";" }] + })); + body = aproditeResult.html; + } + + body = body || props.renderBody(); + var styledComponentsStyles = props.sheet.getStyleElement(); + + // rewind html metas + var head = _reactHelmet2.default.rewind(); + + // is automatically prepended + return _react2.default.createElement( + "html", + _extends({ + lang: "en" + }, head.htmlAttributes.toComponent()), + _react2.default.createElement( + "head", + null, + head.base.toComponent(), + head.title.toComponent(), + _react2.default.createElement("meta", { charSet: "utf-8" }), + _react2.default.createElement("meta", { httpEquiv: "X-UA-Compatible", content: "IE=edge" }), + _react2.default.createElement("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), + head.meta.toComponent(), + head.style.toComponent(), + head.link.toComponent(), + props.css.map(function (file, i) { + return _react2.default.createElement("link", { key: "phenomic.css." + i, rel: "stylesheet", href: file }); + }), + styledComponentsStyles, + head.script.toComponent() + ), + _react2.default.createElement( + "body", + null, + _react2.default.createElement("div", { id: "phenomic", dangerouslySetInnerHTML: { __html: body } }), + props.renderScript(), + props.js.map(function (file, i) { + return _react2.default.createElement("script", { key: "phenomic.js." + i, src: file }); + }) + ) + ); +}; + +Html.propTypes = { + css: require("react").PropTypes.arrayOf(require("react").PropTypes.string).isRequired, + js: require("react").PropTypes.arrayOf(require("react").PropTypes.string).isRequired, + renderBody: require("react").PropTypes.func.isRequired, + renderScript: require("react").PropTypes.func.isRequired, + sheet: require("react").PropTypes.any.isRequired +}; +exports.default = Html; \ No newline at end of file diff --git a/lib/components/Link/index.js b/lib/components/Link/index.js new file mode 100644 index 000000000..1ec83e776 --- /dev/null +++ b/lib/components/Link/index.js @@ -0,0 +1,91 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _react = require("react"); + +var _react2 = _interopRequireDefault(_react); + +var _classnames = require("classnames"); + +var _classnames2 = _interopRequireDefault(_classnames); + +var _reactRouter = require("react-router"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } + +var origin = function origin(location) { + return location.origin || + + // IE does not correctly handle origin, maybe Edge does... + location.protocol + "//" + location.hostname + (location.port ? ":" + location.port : ""); +}; + +function Link(props, _ref) { + var router = _ref.router; + + var to = props.to, + otherProps = _objectWithoutProperties(props, ["to"]); + + var simpleLink = _react2.default.createElement("a", _extends({}, otherProps, { + href: to, + className: props.className + })); + + // static rendering + if (typeof document === "undefined") { + return simpleLink; + } + + var toLink = document.createElement("a"); + toLink.href = to; + + if ( + // parent absolute url with the same domain + // should not be Link + to.indexOf("://") > -1 && to.indexOf(process.env.PHENOMIC_USER_URL) === -1) { + return simpleLink; + } + + if (origin(toLink) === origin(window.location) + // we might want to restrict Link to path including the pathname + // but this will require to preprend pathname to all Links from the + // collection, which sucks. + // If people wants to use Link for a same domain, but in the parent path, + // you will need to includes the entire url, / won't work at it will use + // the react-router basename defined by Phenomic. + // && + // toLink.pathname.includes(process.env.PHENOMIC_USER_PATHNAME) + // toLink.pathname.indexOf(process.env.PHENOMIC_USER_PATHNAME) > -1 + ) { + return _react2.default.createElement(_reactRouter.Link, _extends({}, otherProps, { + to: to, + className: (0, _classnames2.default)(props.className, _defineProperty({}, props.activeClassName, router && (router.isActive({ pathname: props.to }) || router.isActive({ pathname: props.to + "index.html" })) && props.activeClassName)) + })); + } + + return simpleLink; +} + +Link.propTypes = { + children: _react.PropTypes.node, + to: _react.PropTypes.string.isRequired, + className: _react.PropTypes.string, + activeClassName: _react.PropTypes.string +}; + +Link.contextTypes = { + router: _react.PropTypes.object.isRequired +}; + +Link.displayName = "Link"; + +exports.default = Link; \ No newline at end of file diff --git a/lib/components/PageContainer/component.js b/lib/components/PageContainer/component.js new file mode 100644 index 000000000..b37557ebe --- /dev/null +++ b/lib/components/PageContainer/component.js @@ -0,0 +1,288 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _react = require("react"); + +var _react2 = _interopRequireDefault(_react); + +var _reactDom = require("react-dom"); + +var _urlify = require("../../_utils/urlify"); + +var _urlify2 = _interopRequireDefault(_urlify); + +var _catchLinks = require("../../_utils/catch-links"); + +var _catchLinks2 = _interopRequireDefault(_catchLinks); + +var _client = require("../../client"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +/* eslint-disable react/sort-comp */ + + +var logPrefix = "phenomic: PageContainer:"; + +// react-router does not return leading and trailing slashes +// so we need to normalize according to collection data +var splatToUrl = function splatToUrl(string) { + var url = "/" + (0, _urlify2.default)(string); + return url === "//" ? "/" : url; +}; + +function find(collection, pageUrl) { + return collection.find(function (item) { + return item.__url === pageUrl || item.__url === pageUrl + "/" || item.__resourceUrl === pageUrl; + }); +} + +function getBase(location) { + return location.protocol + "//" + location.host + process.env.PHENOMIC_USER_PATHNAME; +} + +function adjustCurrentUrl(location, item, props) { + // adjust url (eg: missing trailing slash) + var currentExactPageUrl = location.href.replace(getBase(location), "/"); + var itemURL = item.__url + location.search + location.hash; + + if (currentExactPageUrl !== itemURL) { + props.logger.info(logPrefix + " replacing by '" + currentExactPageUrl + "' to '" + itemURL + "'"); + if (_client.browserHistory) { + _client.browserHistory.replace(itemURL); + } + } +} + +function getLayout(layout, props) { + if (props.layouts && props.layouts[layout]) { + return props.layouts[layout]; + } +} + +var PageContainer = function (_Component) { + _inherits(PageContainer, _Component); + + function PageContainer() { + _classCallCheck(this, PageContainer); + + return _possibleConstructorReturn(this, (PageContainer.__proto__ || Object.getPrototypeOf(PageContainer)).apply(this, arguments)); + } + + _createClass(PageContainer, [{ + key: "componentWillMount", + value: function componentWillMount() { + var props = this.props; + + if (!getLayout(props.defaultLayout, props)) { + props.logger.error(logPrefix + " default layout \"" + props.defaultLayout + "\" not provided."); + } + this.preparePage(this.props, this.context); + } + }, { + key: "componentDidMount", + value: function componentDidMount() { + this.catchInternalLink(); + } + }, { + key: "componentWillReceiveProps", + value: function componentWillReceiveProps(nextProps) { + this.preparePage(nextProps, this.context); + } + }, { + key: "componentDidUpdate", + value: function componentDidUpdate() { + this.catchInternalLink(); + } + }, { + key: "catchInternalLink", + value: function catchInternalLink() { + var _this2 = this; + + var layoutDOMElement = (0, _reactDom.findDOMNode)(this); + + if (layoutDOMElement) { + var bodyContainers = Array.prototype.slice.call(layoutDOMElement.querySelectorAll(".phenomic-BodyContainer")); + if (!bodyContainers.length) { + bodyContainers = [layoutDOMElement]; + } + + // as soon as we listened to something, we should carefully unlisten + // because + // - it can be listening in the layoutDOMElement (a parent) + // - it can be listening in a might be catched (but they are not supposed to) + if (this.cleanupInternalLinks) { + this.cleanupInternalLinks(); + } + this.cleanupInternalLinks = (0, _catchLinks2.default)(bodyContainers, function (href) { + // do not catch links that are under the current base path + if (href.indexOf(process.env.PHENOMIC_USER_PATHNAME) === -1) { + // === if (!href.includes(process.env.PHENOMIC_USER_PATHNAME)) { + return false; + } + + // lookup in collection by adjusting the link with our stored links + var pageUrl = href + // remove pathname as collection links don't have pathname included + .replace(process.env.PHENOMIC_USER_PATHNAME, "/"); + if (!find(_this2.context.collection, pageUrl + // remove hash for the lookup as it's not necessary + .replace(/#.*/, ""))) { + return false; + } + if (_client.browserHistory) { + _client.browserHistory.push(pageUrl); + } + return true; + }); + } + } + }, { + key: "preparePage", + value: function preparePage(props, context) { + var pageUrl = splatToUrl(props.params.splat); + if (process.env.NODE_ENV !== "production") { + props.logger.info(logPrefix + " '" + pageUrl + "' rendering..."); + } + + var item = find(context.collection, pageUrl); + if (typeof window !== "undefined" && typeof window.location !== "undefined" && item) { + adjustCurrentUrl(window.location, item, props); + } + + var page = props.pages[pageUrl]; + if (!page) { + if (item) { + props.getPage(item.__url, item.__dataUrl); + } else { + props.logger.error(logPrefix + " " + pageUrl + " is a page not found."); + props.setPageNotFound(pageUrl); + } + } else { + if (page.error) { + return; + } + + var Layout = getLayout(page.type, props); + if (page.type !== undefined && !Layout) { + props.logger.error(logPrefix + " Unkown page type: \"" + page.type + "\"" + "component not available in \"layouts\" property." + ("Please check the \"layout\" or \"type\" of page \"" + page + "\" header.")); + } + } + } + }, { + key: "render", + value: function render() { + var props = this.props; + var collection = this.context.collection; + + + var pageUrl = splatToUrl(props.params.splat); + // page url from redux store + var page = props.pages[pageUrl]; + var partialPageHead = collection.find(function (pageData) { + return pageUrl === pageData.__url; + }) || {}; + + if (!page) { + if (process.env.NODE_ENV !== "production") { + props.logger.info(logPrefix + " '" + pageUrl + "' no data"); + } + // return null + } + if (process.env.NODE_ENV !== "production") { + props.logger.info(logPrefix + " '" + pageUrl + "'", page); + } + + if ((typeof page === "undefined" ? "undefined" : _typeof(page)) !== "object" || page.toString() !== "[object Object]") { + props.logger.info(logPrefix + " page " + pageUrl + " should be an object"); + return null; + } + var PageLoading = getLayout("PageLoading", props); + var PageError = getLayout("PageError", props); + var LayoutFallback = getLayout(props.defaultLayout, props); + var Layout = getLayout(partialPageHead.type || partialPageHead.layout || + // page.type is head type||layout too + page.type, props) || LayoutFallback; + + if (page.error) { + if (!PageError) { + return _react2.default.createElement( + "div", + { style: { "text-align": "center" } }, + _react2.default.createElement( + "h1", + null, + page.error + ), + _react2.default.createElement( + "p", + null, + page.errorText + ) + ); + } + return _react2.default.createElement(PageError, page); + } else if (page.isLoading && PageLoading && Layout && !Layout.hasLoadingState) { + return _react2.default.createElement(PageLoading, null); + } else if (Layout) { + return _react2.default.createElement(Layout + // head will be overwritten by "page" + // (since page contains a head when loaded) + , _extends({ head: partialPageHead + }, page)); + } + + if (process.env.NODE_ENV !== "production") { + return _react2.default.createElement( + "div", + null, + "No layout can be rendered. See console for more information." + ); + } + + return null; + } + }]); + + return PageContainer; +}(_react.Component); + +PageContainer.contextTypes = { + collection: _react.PropTypes.arrayOf(_react.PropTypes.object), + layouts: _react.PropTypes.object +}; +PageContainer.defaultProps = { + layouts: {}, + defaultLayout: "Page", + logger: console +}; +PageContainer.propTypes = { + pages: require("react").PropTypes.object.isRequired, + params: require("react").PropTypes.shape({ + splat: require("react").PropTypes.string.isRequired + }).isRequired, + layouts: require("react").PropTypes.object.isRequired, + defaultLayout: require("react").PropTypes.string.isRequired, + getPage: require("react").PropTypes.func.isRequired, + setPageNotFound: require("react").PropTypes.func.isRequired, + logger: require("react").PropTypes.object.isRequired +}; +exports.default = PageContainer; \ No newline at end of file diff --git a/lib/components/PageContainer/index.js b/lib/components/PageContainer/index.js new file mode 100644 index 000000000..867d6ba39 --- /dev/null +++ b/lib/components/PageContainer/index.js @@ -0,0 +1,35 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _reactRedux = require("react-redux"); + +var _pages = require("../../redux/modules/pages"); + +var pageActions = _interopRequireWildcard(_pages); + +var _component = require("./component"); + +var _component2 = _interopRequireDefault(_component); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +// eslint-disable-next-line import/no-namespace +exports.default = (0, _reactRedux.connect)(function (_ref) { + var pages = _ref.pages; + + return { pages: pages }; +}, function (dispatch) { + return { + getPage: function getPage() { + return dispatch(pageActions.get.apply(pageActions, arguments)); + }, + setPageNotFound: function setPageNotFound() { + return dispatch(pageActions.setNotFound.apply(pageActions, arguments)); + } + }; +})(_component2.default); \ No newline at end of file diff --git a/lib/configurator/definitions.js b/lib/configurator/definitions.js new file mode 100644 index 000000000..14b0a041f --- /dev/null +++ b/lib/configurator/definitions.js @@ -0,0 +1,96 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = { + "cwd": { + type: "string", + description: "base directory (eg: where 'source' and 'destination' should be", + default: process.cwd() + }, + "source": { + type: "string", + description: "source folder of your content (md, assets, etc)", + default: "content" + }, + "destination": { + type: "string", + description: "destination folder of your build", + default: "dist" + }, + "assets": { + // types: string, objects, boolean and falsy value + description: "path to static assets (images, media etc)", + default: "assets" + }, + "offline": { + // types: boolean, object + description: "flag to enable offline usage via appcache and service worker" + }, + "force-offline": { + type: "boolean", + description: "flag to force offline mode (for development)", + default: false + }, + "CNAME": { + type: "boolean", + description: "flag to enable automatic generation of a CNAME file", + default: false + }, + "nojekyll": { + type: "boolean", + description: "flag to generate a .nojekyll file, to avoid Jekyll automatic step on" + "GitHub Pages", + default: true + }, + "devHost": { + type: "string", + description: "host used during development", + default: "0.0.0.0" + }, + "devPort": { + type: "number", + description: "port used during development", + default: 3333 + }, + "verbose": { + type: "boolean", + description: "flag to enable a more verbose output", + default: false + }, + "dev": { + type: "boolean", + description: "flag to enable dev mode (hot loading)", + default: false + }, + "production": { + type: "boolean", + description: "flag to enable production (optimized) mode", + default: false + }, + "static": { + type: "boolean", + description: "flag to enable static build", + default: false + }, + "server": { + type: "boolean", + description: "flag to enable development server", + default: false + }, + "open": { + type: "boolean", + description: "flag to automatically open development server", + default: true + }, + "cache": { + type: "boolean", + description: "flag to enable hard cache for webpack " + "(hard-source-webpack-plugin)", + default: false + }, + "clientScripts": { + type: "boolean", + description: "flag to disable client side JavaScript in HTML files", + default: true + } +}; \ No newline at end of file diff --git a/lib/configurator/index.js b/lib/configurator/index.js new file mode 100644 index 000000000..99ef1ef47 --- /dev/null +++ b/lib/configurator/index.js @@ -0,0 +1,96 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +exports.default = config; +exports.testConfig = testConfig; + +var _path = require("path"); + +var _chalk = require("chalk"); + +var _chalk2 = _interopRequireDefault(_chalk); + +var _yargs = require("./yargs.js"); + +var _yargs2 = _interopRequireDefault(_yargs); + +var _definitions = require("./definitions.js"); + +var _definitions2 = _interopRequireDefault(_definitions); + +var _minimalValidator = require("./minimal-validator.js"); + +var _minimalValidator2 = _interopRequireDefault(_minimalValidator); + +var _validators = require("./validators.js"); + +var validators = _interopRequireWildcard(_validators); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } +// eslint-disable-next-line import/no-namespace + + +function config() { + var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + _ref$argv = _ref.argv, + argv = _ref$argv === undefined ? [] : _ref$argv, + _ref$pkg = _ref.pkg, + pkg = _ref$pkg === undefined ? {} : _ref$pkg; + + var userJSConfig = pkg.phenomic || {}; + + _yargs2.default.strict(); + + var defaultAndCLIconfig = _yargs2.default.parse(argv); + + // delete unwanted yargs parameters + delete defaultAndCLIconfig.$0; + delete defaultAndCLIconfig._; + delete defaultAndCLIconfig.help; + delete defaultAndCLIconfig.version; + + // validate user parameters + var errors = [].concat(_toConsumableArray((0, _minimalValidator2.default)(userJSConfig, _definitions2.default))); + + var config = _extends({}, defaultAndCLIconfig, userJSConfig, process.env.TESTING && userJSConfig.cwd === undefined && { + cwd: (0, _path.join)(__dirname, "__tests__") + }); + + // validation/adjustement for each options + Object.keys(validators).forEach(function (key) { + // eslint-disable-next-line import/namespace + validators[key]({ + pkg: pkg, + config: config, + definitions: _definitions2.default, + errors: errors + }); + }); + + if (errors.length > 0) { + var errorMessage = "\n⚠️ " + "phenomic: " + _chalk2.default.yellow("your config is invalid. Please fix the errors:") + "\n\n" + _chalk2.default.red("- " + errors.join("\n- ")) + "\n\n" + _chalk2.default.yellow("See 'Configuration' section in documentation.") + " " + "https://phenomic.io/docs/usage/configuration/"; + if (process.env.TESTING) { + throw new Error(errorMessage); + } + // else + console.error(errorMessage); + process.exit(1); + } + + return config; +} + +function testConfig(cfg) { + return config({ + pkg: { phenomic: cfg } + }); +} \ No newline at end of file diff --git a/lib/configurator/minimal-validator.js b/lib/configurator/minimal-validator.js new file mode 100644 index 000000000..bb11b4be2 --- /dev/null +++ b/lib/configurator/minimal-validator.js @@ -0,0 +1,21 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +exports.default = function (config, definitions) { + var errors = []; + + Object.keys(config).forEach(function (key) { + if (!definitions[key]) { + errors.push("Unknow option '" + key + "'."); + } else if (definitions[key].type !== undefined && definitions[key].type !== _typeof(config[key])) { + errors.push("Wrong type for '" + key + "': expected '" + definitions[key].type + "', " + ("got '" + _typeof(config[key]) + "'.")); + } + }); + + return errors; +}; \ No newline at end of file diff --git a/lib/configurator/validators.js b/lib/configurator/validators.js new file mode 100644 index 000000000..3423d3a7a --- /dev/null +++ b/lib/configurator/validators.js @@ -0,0 +1,35 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.production = exports.offline = exports.devPort = exports.baseUrl = exports.assets = undefined; + +var _assets2 = require("./validators/assets.js"); + +var _assets3 = _interopRequireDefault(_assets2); + +var _baseUrl2 = require("./validators/baseUrl.js"); + +var _baseUrl3 = _interopRequireDefault(_baseUrl2); + +var _devPort2 = require("./validators/devPort.js"); + +var _devPort3 = _interopRequireDefault(_devPort2); + +var _offline2 = require("./validators/offline.js"); + +var _offline3 = _interopRequireDefault(_offline2); + +var _production2 = require("./validators/production.js"); + +var _production3 = _interopRequireDefault(_production2); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.assets = _assets3.default; +exports.baseUrl = _baseUrl3.default; +exports.devPort = _devPort3.default; +exports.offline = _offline3.default; // ! must be after baseUrl + +exports.production = _production3.default; \ No newline at end of file diff --git a/lib/configurator/validators/assets.js b/lib/configurator/validators/assets.js new file mode 100644 index 000000000..d1329987e --- /dev/null +++ b/lib/configurator/validators/assets.js @@ -0,0 +1,69 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +exports.default = function (_ref) { + var config = _ref.config, + errors = _ref.errors; + + // Prepare config.assets path and route + if (config.assets) { + + // normalize simple string options + if (["boolean", "string", "object"].indexOf(_typeof(config.assets)) < 0) { + errors.push("You provided a " + _typeof(config.assets) + " for 'assets' option." + "This option accept a boolean value, a string, or an object."); + } else if (_typeof(config.assets) === "object" && (typeof config.assets.path !== "string" || typeof config.assets.route !== "string")) { + var configAssets = config.assets; + errors.push("You provided an object for 'assets' option." + "You need to provide 2 keys: " + "'path' (string, path of your assets, relative to 'source') " + "and 'route' (string, path of your assets folder in the destination)." + "\n" + "You provided the following keys: " + Object.keys(configAssets).map(function (k) { + return "'" + k + "' (" + _typeof(configAssets[k]) + ")"; + }).toString()); + } else { + if (typeof config.assets === "string") { + config.assets = { + path: config.assets, + route: config.assets + }; + } else if (typeof config.assets === "boolean") { + // === true + config.assets = { + path: _definitions2.default.assets.default, + route: _definitions2.default.assets.default + }; + } + + var _configAssets = config.assets; + // adjust path and validate + config.assets = { + path: (0, _path.join)(config.cwd, config.source, _configAssets.path), + route: _configAssets.route + }; + + // Test folder + try { + var stats = _fs2.default.lstatSync(config.assets.path); + if (!stats.isDirectory()) { + // Just throw a dump error + throw new Error("This is not a folder"); + } + } catch (e) { + errors.push(config.assets.path + " doesn't exist or isn't a folder. " + "Please check your 'assets' configuration. " + "Note that if you don't need this option, " + "you can set it up to `false`."); + } + } + } +}; + +var _path = require("path"); + +var _fs = require("fs"); + +var _fs2 = _interopRequireDefault(_fs); + +var _definitions = require("../definitions.js"); + +var _definitions2 = _interopRequireDefault(_definitions); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } \ No newline at end of file diff --git a/lib/configurator/validators/baseUrl.js b/lib/configurator/validators/baseUrl.js new file mode 100644 index 000000000..9fee91bd1 --- /dev/null +++ b/lib/configurator/validators/baseUrl.js @@ -0,0 +1,32 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +exports.default = function (_ref) { + var pkg = _ref.pkg, + config = _ref.config; + + var devUrl = "http://" + config.devHost + ":" + config.devPort + "/"; + var prodBaseUrl = (0, _url.parse)(pkg.homepage ? pkg.homepage : devUrl); + config.baseUrl = config.production ? prodBaseUrl : _extends({}, (0, _url.parse)(devUrl), { + // get base from prod url + pathname: prodBaseUrl.path ? prodBaseUrl.path : "/" + }); + + config.baseUrl = (0, _index2.default)(config.baseUrl); + + // Set basename to process.env for universal usage + process.env.PHENOMIC_USER_PATHNAME = config.baseUrl.pathname; +}; + +var _url = require("url"); + +var _index = require("../../_utils/normalize-base-url/index.js"); + +var _index2 = _interopRequireDefault(_index); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } \ No newline at end of file diff --git a/lib/configurator/validators/devPort.js b/lib/configurator/validators/devPort.js new file mode 100644 index 000000000..17e8b5cb4 --- /dev/null +++ b/lib/configurator/validators/devPort.js @@ -0,0 +1,18 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.default = function (_ref) { + var config = _ref.config, + errors = _ref.errors; + + var port = Math.trunc(config.devPort); + + if (port > 0) { + config.devPort = port; + } else { + errors.push("`devPort` must be a legal http port number"); + } +}; \ No newline at end of file diff --git a/lib/configurator/validators/offline.js b/lib/configurator/validators/offline.js new file mode 100644 index 000000000..513536988 --- /dev/null +++ b/lib/configurator/validators/offline.js @@ -0,0 +1,110 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.defaultOfflineConfig = undefined; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _url = require("url"); + +var _chalk = require("chalk"); + +var _log = require("../../_utils/log"); + +var _log2 = _interopRequireDefault(_log); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var defaultOfflineConfig = exports.defaultOfflineConfig = { + serviceWorker: true, + appcache: true, + cachePatterns: { + onInstall: ["/", "phenomic.*"], + afterInstall: ["**", ":assets:"], + onDemand: [], + excludes: ["**/.*", "**/*.map", "**/*.html"] + } +}; + +var knownKeys = Object.keys(defaultOfflineConfig); + +exports.default = function (_ref) { + var pkg = _ref.pkg, + config = _ref.config, + errors = _ref.errors; + + + if (!config.offline) { + config.offline = false; + return; + } + + if (config.offline === true) { + config.offlineConfig = defaultOfflineConfig; + } else if (_typeof(config.offline) !== "object") { + var keys = Object.keys(defaultOfflineConfig); + errors.push("Your provided an '" + _typeof(config.offline) + "'" + "for 'phenomic.offline'." + "This option accepts a boolean or an object" + ("with " + keys.length + " keys: ") + keys.join(", ")); + + return; + } else { + var userOfflineConfig = config.offline; + + var allKeys = Object.keys(userOfflineConfig); + var incorrectKeys = allKeys.filter(function (key) { + return knownKeys.indexOf(key) === -1; + }); + if (incorrectKeys.length) { + errors.push("You provided some key(s) " + "for 'phenomic.offline' option " + "that are not recognized " + ("(" + incorrectKeys.join(", ") + "). ") + ""); + } + + if (typeof userOfflineConfig.serviceWorker !== "boolean") { + errors.push("You provided an incorrect type" + (" ('" + _typeof(userOfflineConfig.serviceWorker) + "') ") + "for 'phenomic.offline.serviceWorker' option. " + "This option accepts a boolean value."); + } + + if (typeof userOfflineConfig.appcache !== "boolean") { + errors.push("You provided an incorrect type" + (" ('" + _typeof(userOfflineConfig.appcache) + "') ") + "for 'phenomic.offline.appcache' option. " + "This option accepts a boolean value."); + } + + // Validate patterns + var cachePatternsKeys = Object.keys(defaultOfflineConfig.cachePatterns); + var error = void 0; + if (_typeof(userOfflineConfig.cachePatterns) !== "object") { + error = "You provided an incorrect type" + (" ('" + _typeof(userOfflineConfig.cachePatterns) + "') ") + "for 'phenomic.offline.cachePatterns' option. "; + } else { + var _incorrectKeys = Object.keys(userOfflineConfig.cachePatterns).filter(function (key) { + return !(cachePatternsKeys.indexOf(key) > -1) || !Array.isArray(userOfflineConfig.cachePatterns[key]); + }); + if (_incorrectKeys.length) { + error = "You provided some key(s) " + "for 'phenomic.offline.cachePatterns' option " + "that are not recognized or with incorrect types " + ("(" + _incorrectKeys.join(", ") + "). ") + ""; + } + } + if (error) { + errors.push(error + "This option accepts a object with " + ("with " + cachePatternsKeys.length + " keys: ") + cachePatternsKeys.join(", ") + " " + "that accept array of glob patterns."); + } + + // Merge config with default config + config.offlineConfig = _extends({}, defaultOfflineConfig, userOfflineConfig, { + cachePatterns: _extends({}, defaultOfflineConfig.cachePatterns, userOfflineConfig.cachePatterns) + }); + } + + if (pkg.homepage && (0, _url.parse)(pkg.homepage).protocol !== "https:" && config.offlineConfig.serviceWorker) { + console.warn((0, _chalk.yellow)("ServiceWorker (for Offline access) only works with HTTPS protocol." + "You are currently using HTTP, so ServiceWorker will be ignored by " + "browsers.")); + } + + // Disable offline for development if user defined offline config + if (config.dev && config.offline && !config.forceOffline) { + config.offline = false; + config.offlineConfig = { + serviceWorker: false, + appcache: false, + cachePatterns: {} + }; + (0, _log2.default)((0, _chalk.gray)("Offline support disabled during development " + "(to avoid some false positives)"), "warning"); + return; + } +}; \ No newline at end of file diff --git a/lib/configurator/validators/production.js b/lib/configurator/validators/production.js new file mode 100644 index 000000000..3de3428d1 --- /dev/null +++ b/lib/configurator/validators/production.js @@ -0,0 +1,18 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.default = function (_ref) { + var pkg = _ref.pkg, + config = _ref.config, + errors = _ref.errors; + + if (config.production) { + process.env.NODE_ENV = "production"; + if (!pkg.homepage) { + errors.push("Your package.json require a 'homepage' field."); + } + } +}; \ No newline at end of file diff --git a/lib/configurator/yargs.js b/lib/configurator/yargs.js new file mode 100644 index 000000000..a5360d788 --- /dev/null +++ b/lib/configurator/yargs.js @@ -0,0 +1,53 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _yargs = require("yargs"); + +var _yargs2 = _interopRequireDefault(_yargs); + +var _package = require("../../package.json"); + +var _definitions = require("./definitions.js"); + +var _definitions2 = _interopRequireDefault(_definitions); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// import * as validators from "./validators.js" + +_yargs2.default.version(function () { + return _package.version; +}).help().showHelpOnFail().epilogue("For more information about the configuration, " + "https://phenomic.io/"); + +Object.keys(_definitions2.default).forEach(function (optName) { + var option = _definitions2.default[optName]; + + // for now all flags are common + _yargs2.default.global(optName); + + // check type + // eg: yargs.boolean(someflag) to ensure that the type is correct + if (option.type && _yargs2.default[option.type]) { + _yargs2.default[option.type](optName); + } + + if (option.default) { + _yargs2.default.default(optName, option.default); + } + + if (option.description) { + _yargs2.default.describe(optName, option.description); + } + + // made by hand for now, we might revisit option this later + // if (validators[optName]) { + // yargs.check(() => { + // + // }) + // } +}); + +exports.default = _yargs2.default; \ No newline at end of file diff --git a/lib/enhance-collection/index.js b/lib/enhance-collection/index.js new file mode 100644 index 000000000..6f394ebd6 --- /dev/null +++ b/lib/enhance-collection/index.js @@ -0,0 +1,192 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +exports.filter = filter; +exports.sort = sort; +exports.reverse = reverse; +exports.limit = limit; +exports.addSiblingReferences = addSiblingReferences; + +var _lruMemoize = require("lru-memoize"); + +var _lruMemoize2 = _interopRequireDefault(_lruMemoize); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +var defaultConsole = console; + +exports.default = (0, _lruMemoize2.default)(100)(enhanceCollection); + + +function enhanceCollection(collection, options) { + var console = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : defaultConsole; + + options = _extends({}, options); + + if (options.filter) { + collection = filter(collection, [options.filter], console); + } + + if (options.filters) { + collection = filter(collection, options.filters, console); + } + + if (options.sort) { + collection = sort(collection, options.sort); + } + + if (options.reverse) { + collection = reverse(collection); + } + + if (options.limit) { + collection = limit(collection, options.limit); + } + + if (options.addSiblingReferences) { + collection = addSiblingReferences(collection); + } + + return collection; +} + +function filter(collection, filters) { + var console = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : defaultConsole; + + return collection.reduce(function (acc, item) { + var include = true; + + var _loop = function _loop(_filter) { + switch (typeof _filter === "undefined" ? "undefined" : _typeof(_filter)) { + case "function": + { + var flag = _filter(item); + if (typeof flag !== "boolean") { + console.warn("Function passed to filter item in 'enhanceCollection' should " + "return a boolean value. \n" + ("You returned '" + (typeof flag === "undefined" ? "undefined" : _typeof(flag)) + "'.")); + } + if (!flag) { + include = false; + } + break; + } + case "object": + { + var keys = Object.keys(_filter); + if (!keys.reduce(function (acc, key) { + return acc && (typeof _filter[key] === "string" && item[key] === _filter[key] || _filter[key] instanceof RegExp && item[key] && item[key].match(_filter[key])); + }, true)) { + include = false; + } + break; + } + case "string": + default: + if (!item[_filter]) { + include = false; + } + break; + } + + // break asap + if (!include) { + return "break"; + } + }; + + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = filters[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var _filter = _step.value; + + var _ret = _loop(_filter); + + if (_ret === "break") break; + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + if (include) { + acc.push(item); + } + + return acc; + }, []); +} + +function sort(collection) { + var sort = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "date"; + + collection = [].concat(_toConsumableArray(collection)); + + if (typeof sort === "function") { + collection.sort(sort); + } else { + collection.sort(function (a, b) { + a = a[sort]; + b = b[sort]; + if (!a && !b) return 0; + if (!a) return -1; + if (!b) return 1; + if (b > a) return -1; + if (a > b) return 1; + return 0; + }); + } + + return collection; +} + +function reverse(collection) { + collection = [].concat(_toConsumableArray(collection)); + collection.reverse(); + return collection; +} + +function limit(collection, limit) { + return collection.slice(0, limit); +} + +function addSiblingReferences(collection) { + var last = collection.length - 1; + // TODO: Use commented code when flow can understand it + // return collection.map((item, i) => ({ + // ...item, + // ...(0 != i) && { previous: collection[i-1] }, + // ...(last != i) && { next: collection[i+1] }, + // })) + return collection.map(function (item, i) { + var newItem = _extends({}, item); + if (0 != i) { + newItem.previous = collection[i - 1]; + } + if (last != i) { + newItem.next = collection[i + 1]; + } + + return newItem; + }); +} \ No newline at end of file diff --git a/lib/eslint-config-recommended/index.js b/lib/eslint-config-recommended/index.js new file mode 100644 index 000000000..04ccc638b --- /dev/null +++ b/lib/eslint-config-recommended/index.js @@ -0,0 +1,20 @@ +"use strict"; + +module.exports = { + "parser": "babel-eslint", + "extends": ["eslint:recommended", "plugin:react/recommended"], + "env": { + "browser": true, + "node": true + }, + "plugins": ["react"], + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true, + "generators": true, + "experimentalObjectRestSpread": true + } + } +}; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 000000000..847263754 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,45 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _PageContainer = require("./components/PageContainer"); + +Object.defineProperty(exports, "PageContainer", { + enumerable: true, + get: function get() { + return _interopRequireDefault(_PageContainer).default; + } +}); + +var _BodyContainer = require("./components/BodyContainer"); + +Object.defineProperty(exports, "BodyContainer", { + enumerable: true, + get: function get() { + return _interopRequireDefault(_BodyContainer).default; + } +}); + +var _Link = require("./components/Link"); + +Object.defineProperty(exports, "Link", { + enumerable: true, + get: function get() { + return _interopRequireDefault(_Link).default; + } +}); + +var _urlJoin = require("url-join"); + +Object.defineProperty(exports, "joinUri", { + enumerable: true, + get: function get() { + return _interopRequireDefault(_urlJoin).default; + } +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var phenomicLoader = exports.phenomicLoader = "phenomic/lib/loader"; \ No newline at end of file diff --git a/lib/loader-feed-webpack-plugin/feed.js b/lib/loader-feed-webpack-plugin/feed.js new file mode 100644 index 000000000..59b0c4f59 --- /dev/null +++ b/lib/loader-feed-webpack-plugin/feed.js @@ -0,0 +1,47 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; // @todo @ flow + +var _url = require("url"); + +var _url2 = _interopRequireDefault(_url); + +var _rss = require("rss"); + +var _rss2 = _interopRequireDefault(_rss); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.default = function () { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + var _feedOptions$options = _extends({ + feedOptions: {} + }, options), + feedOptions = _feedOptions$options.feedOptions, + destination = _feedOptions$options.destination, + collection = _feedOptions$options.collection, + xmlOptions = _feedOptions$options.xmlOptions; + + if (!feedOptions.site_url) { + throw new Error("feed site_url must be configured"); + } + + if (feedOptions.feed_url == null) { + feedOptions.feed_url = _url2.default.resolve(feedOptions.site_url, destination); + } + + var feed = new _rss2.default(feedOptions); + collection.forEach(function (item) { + feed.item(_extends({}, item, { + url: item.__url ? _url2.default.resolve(feedOptions.site_url, item.__url) : undefined + + })); + }); + + return feed.xml(xmlOptions); +}; \ No newline at end of file diff --git a/lib/loader-feed-webpack-plugin/index.js b/lib/loader-feed-webpack-plugin/index.js new file mode 100644 index 000000000..ce1e1504f --- /dev/null +++ b/lib/loader-feed-webpack-plugin/index.js @@ -0,0 +1,69 @@ +"use strict"; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +// module.exports is used +// eslint-disable-next-line import/default + + +var _webpackSources = require("webpack-sources"); + +var _enhanceCollection = require("../enhance-collection"); + +var _enhanceCollection2 = _interopRequireDefault(_enhanceCollection); + +var _minify = require("../loader/minify.js"); + +var _minify2 = _interopRequireDefault(_minify); + +var _plugin = require("../loader/plugin.js"); + +var _plugin2 = _interopRequireDefault(_plugin); + +var _feed = require("./feed"); + +var _feed2 = _interopRequireDefault(_feed); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function PhenomicLoaderFeedWebpackPlugin(options) { + this.options = options; +} + +PhenomicLoaderFeedWebpackPlugin.prototype.apply = function (compiler) { + var _this = this; + + compiler.plugin("compilation", function (compilation /* , params */) { + compilation.plugin("additional-assets", function (callback) { + if (!_plugin2.default.collection) { + throw new Error("Missing Phenomic collection in webpack compilation object. " + "This probably means you are playing with fire."); + } + var collection = (0, _minify2.default)(_plugin2.default.collection); + + var feeds = _this.options.feeds || []; + var feedsOptions = _this.options.feedsOptions || {}; + Object.keys(feeds).forEach(function (name) { + var _feeds$name = feeds[name], + feedOptions = _feeds$name.feedOptions, + collectionOptions = _feeds$name.collectionOptions; + + compilation.assets[name] = new _webpackSources.RawSource((0, _feed2.default)({ + feedOptions: _extends({}, feedsOptions, feedOptions), + destination: name, + collection: (0, _enhanceCollection2.default)(collection, collectionOptions).map(function (item) { + var fullItem = _plugin2.default.collection.find(function (fullItem) { + return item.__url === fullItem.__url; + }); + return _extends({}, item, { + // null should not happen, but flow ask for secure code :) + description: fullItem ? fullItem.body : null + }); + }) + })); + }); + + callback(); + }); + }); +}; + +module.exports = PhenomicLoaderFeedWebpackPlugin; \ No newline at end of file diff --git a/lib/loader-plugin-init-body-property-from-content/index.js b/lib/loader-plugin-init-body-property-from-content/index.js new file mode 100644 index 000000000..20f1cded4 --- /dev/null +++ b/lib/loader-plugin-init-body-property-from-content/index.js @@ -0,0 +1,16 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +exports.default = function (_ref) { + var result = _ref.result, + frontMatter = _ref.frontMatter; + + return _extends({}, result, { + body: frontMatter.content + }); +}; \ No newline at end of file diff --git a/lib/loader-plugin-init-head-property-from-config/index.js b/lib/loader-plugin-init-head-property-from-config/index.js new file mode 100644 index 000000000..4c34261a6 --- /dev/null +++ b/lib/loader-plugin-init-head-property-from-config/index.js @@ -0,0 +1,16 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +exports.default = function (_ref) { + var result = _ref.result, + options = _ref.options; + + return _extends({}, result, options && options.defaultHead && { + head: _extends({}, options.defaultHead, result.head) + }); +}; \ No newline at end of file diff --git a/lib/loader-plugin-init-head-property-from-content/index.js b/lib/loader-plugin-init-head-property-from-content/index.js new file mode 100644 index 000000000..035da5b72 --- /dev/null +++ b/lib/loader-plugin-init-head-property-from-content/index.js @@ -0,0 +1,16 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +exports.default = function (_ref) { + var result = _ref.result, + frontMatter = _ref.frontMatter; + + return _extends({}, result, { + head: _extends({}, result.head, JSON.parse(JSON.stringify(frontMatter.data))) + }); +}; \ No newline at end of file diff --git a/lib/loader-plugin-init-raw-property-from-content/index.js b/lib/loader-plugin-init-raw-property-from-content/index.js new file mode 100644 index 000000000..06024656f --- /dev/null +++ b/lib/loader-plugin-init-raw-property-from-content/index.js @@ -0,0 +1,16 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +exports.default = function (_ref) { + var result = _ref.result, + frontMatter = _ref.frontMatter; + + return _extends({}, result, { + raw: frontMatter.orig + }); +}; \ No newline at end of file diff --git a/lib/loader-plugin-init-rawBody-property-from-content/index.js b/lib/loader-plugin-init-rawBody-property-from-content/index.js new file mode 100644 index 000000000..0745bc495 --- /dev/null +++ b/lib/loader-plugin-init-rawBody-property-from-content/index.js @@ -0,0 +1,16 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +exports.default = function (_ref) { + var result = _ref.result, + frontMatter = _ref.frontMatter; + + return _extends({}, result, { + rawBody: frontMatter.content + }); +}; \ No newline at end of file diff --git a/lib/loader-plugin-markdown-init-head.description-property-from-content/index.js b/lib/loader-plugin-markdown-init-head.description-property-from-content/index.js new file mode 100644 index 000000000..218be63ac --- /dev/null +++ b/lib/loader-plugin-markdown-init-head.description-property-from-content/index.js @@ -0,0 +1,56 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _remark = require("remark"); + +var _remark2 = _interopRequireDefault(_remark); + +var _stripMarkdown = require("strip-markdown"); + +var _stripMarkdown2 = _interopRequireDefault(_stripMarkdown); + +var _prune = require("./prune"); + +var _prune2 = _interopRequireDefault(_prune); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var defaultOpts = { + pruneLength: 140, + pruneString: "…" +}; + +function description(text) { + var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + opts = _extends({}, defaultOpts, opts); + + if (opts.pruneLength === 0) { + console.warn("You defined 'description.pruneLength' of phenomic loader " + "with an zero value. This does not make sense, " + ("so the default value " + defaultOpts.pruneLength + " has been used.")); + + opts.pruneLength = defaultOpts.pruneLength; + } + + return (0, _prune2.default)((0, _remark2.default)().use(_stripMarkdown2.default).process(text).toString().replace(/\n+/g, " ") // Avoid useless new lines + .trim(), opts.pruneLength, opts.pruneString); +} + +exports.default = function (_ref) { + var result = _ref.result, + frontMatter = _ref.frontMatter, + options = _ref.options; + + if (result.head && result.head.description) { + return result; + } + return _extends({}, result, { + head: _extends({}, result.head, { + description: description(frontMatter.content, options) + }) + }); +}; \ No newline at end of file diff --git a/lib/loader-plugin-markdown-init-head.description-property-from-content/prune/index.js b/lib/loader-plugin-markdown-init-head.description-property-from-content/prune/index.js new file mode 100644 index 000000000..15af4c48c --- /dev/null +++ b/lib/loader-plugin-markdown-init-head.description-property-from-content/prune/index.js @@ -0,0 +1,17 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.default = function (s, maxLength) { + var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "…"; + + var trimmed = s.substr(0, maxLength); + + if (trimmed === s) { + return s; + } + + return trimmed.substr(0, Math.min(trimmed.length, trimmed.lastIndexOf(" "))) + end; +}; \ No newline at end of file diff --git a/lib/loader-plugin-markdown-transform-body-property-to-html/index.js b/lib/loader-plugin-markdown-transform-body-property-to-html/index.js new file mode 100644 index 000000000..984fd8631 --- /dev/null +++ b/lib/loader-plugin-markdown-transform-body-property-to-html/index.js @@ -0,0 +1,72 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _remark = require("remark"); + +var _remark2 = _interopRequireDefault(_remark); + +var _remarkSlug = require("remark-slug"); + +var _remarkSlug2 = _interopRequireDefault(_remarkSlug); + +var _remarkAutolinkHeadings = require("remark-autolink-headings"); + +var _remarkAutolinkHeadings2 = _interopRequireDefault(_remarkAutolinkHeadings); + +var _remarkHighlight = require("remark-highlight.js"); + +var _remarkHighlight2 = _interopRequireDefault(_remarkHighlight); + +var _remarkToc = require("remark-toc"); + +var _remarkToc2 = _interopRequireDefault(_remarkToc); + +var _remarkHtml = require("remark-html"); + +var _remarkHtml2 = _interopRequireDefault(_remarkHtml); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function mdify(text) { + return (0, _remark2.default)() + // https://github.com/wooorm/remark-slug + .use(_remarkSlug2.default) + + // https://github.com/ben-eb/remark-autolink-headings + .use(_remarkAutolinkHeadings2.default, { + content: { + type: "text", + value: "#" + }, + linkProperties: { + className: "phenomic-HeadingAnchor" + } + }) + + // https://github.com/wooorm/remark-html + .use(_remarkHtml2.default, { entities: "escape" }) + + // https://github.com/ben-eb/remark-highlight.js + .use(_remarkHighlight2.default) + + // https://github.com/wooorm/remark-toc + .use(_remarkToc2.default) + + // render + .process(text, { + commonmark: true + }).toString(); +} + +exports.default = function (_ref) { + var result = _ref.result; + + return _extends({}, result, { + body: mdify(result.body) + }); +}; \ No newline at end of file diff --git a/lib/loader-preset-default/index.js b/lib/loader-preset-default/index.js new file mode 100644 index 000000000..dba4250a4 --- /dev/null +++ b/lib/loader-preset-default/index.js @@ -0,0 +1,23 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _loaderPluginInitHeadPropertyFromConfig = require("../loader-plugin-init-head-property-from-config"); + +var _loaderPluginInitHeadPropertyFromConfig2 = _interopRequireDefault(_loaderPluginInitHeadPropertyFromConfig); + +var _loaderPluginInitHeadPropertyFromContent = require("../loader-plugin-init-head-property-from-content"); + +var _loaderPluginInitHeadPropertyFromContent2 = _interopRequireDefault(_loaderPluginInitHeadPropertyFromContent); + +var _loaderPluginInitBodyPropertyFromContent = require("../loader-plugin-init-body-property-from-content"); + +var _loaderPluginInitBodyPropertyFromContent2 = _interopRequireDefault(_loaderPluginInitBodyPropertyFromContent); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var plugins = [_loaderPluginInitHeadPropertyFromConfig2.default, _loaderPluginInitHeadPropertyFromContent2.default, _loaderPluginInitBodyPropertyFromContent2.default]; + +exports.default = plugins; \ No newline at end of file diff --git a/lib/loader-preset-markdown/index.js b/lib/loader-preset-markdown/index.js new file mode 100644 index 000000000..3f4945d12 --- /dev/null +++ b/lib/loader-preset-markdown/index.js @@ -0,0 +1,23 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _loaderPresetDefault = require("../loader-preset-default"); + +var _loaderPresetDefault2 = _interopRequireDefault(_loaderPresetDefault); + +var _loaderPluginMarkdownInitHead = require("../loader-plugin-markdown-init-head.description-property-from-content"); + +var _loaderPluginMarkdownInitHead2 = _interopRequireDefault(_loaderPluginMarkdownInitHead); + +var _loaderPluginMarkdownTransformBodyPropertyToHtml = require("../loader-plugin-markdown-transform-body-property-to-html"); + +var _loaderPluginMarkdownTransformBodyPropertyToHtml2 = _interopRequireDefault(_loaderPluginMarkdownTransformBodyPropertyToHtml); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +exports.default = [].concat(_toConsumableArray(_loaderPresetDefault2.default), [_loaderPluginMarkdownInitHead2.default, _loaderPluginMarkdownTransformBodyPropertyToHtml2.default]); \ No newline at end of file diff --git a/lib/loader-sitemap-webpack-plugin/index.js b/lib/loader-sitemap-webpack-plugin/index.js new file mode 100644 index 000000000..07d14a25d --- /dev/null +++ b/lib/loader-sitemap-webpack-plugin/index.js @@ -0,0 +1,63 @@ +"use strict"; + +var _webpackSources = require("webpack-sources"); + +var _sitemap = require("sitemap"); + +var _sitemap2 = _interopRequireDefault(_sitemap); + +var _enhanceCollection = require("../enhance-collection"); + +var _enhanceCollection2 = _interopRequireDefault(_enhanceCollection); + +var _minify = require("../loader/minify.js"); + +var _minify2 = _interopRequireDefault(_minify); + +var _plugin = require("../loader/plugin.js"); + +var _plugin2 = _interopRequireDefault(_plugin); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function PhenomicLoaderSitemapWebpackPlugin(options) { + this.options = options; +} +// module.exports is used +// eslint-disable-next-line import/default + + +PhenomicLoaderSitemapWebpackPlugin.prototype.apply = function (compiler) { + var _this = this; + + compiler.plugin("compilation", function (compilation /* , params */) { + compilation.plugin("additional-assets", function (callback) { + if (!_plugin2.default.collection) { + throw new Error("Missing Phenomic collection in webpack compilation object. " + "This probably means you are playing with fire."); + } + + var collection = (0, _minify2.default)(_plugin2.default.collection); + + var _ref = _this.options || {}, + site_url = _ref.site_url, + collectionOptions = _ref.collectionOptions; + + if (!site_url) { + throw new Error("Missing `site_url` option in sitemap configuration. "); + } + + var sitemap = _sitemap2.default.createSitemap({ + hostname: site_url, + urls: (0, _enhanceCollection2.default)(collection, collectionOptions).map(function (item) { + return { url: item.__url }; + }) + }); + + compilation.assets["sitemap.xml"] = new _webpackSources.RawSource(sitemap.toString()); + + callback(); + }); + }); +}; + +module.exports = PhenomicLoaderSitemapWebpackPlugin; \ No newline at end of file diff --git a/lib/loader/index.js b/lib/loader/index.js new file mode 100644 index 000000000..1bffaf9cc --- /dev/null +++ b/lib/loader/index.js @@ -0,0 +1,95 @@ +"use strict"; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _fs = require("fs"); + +var _fs2 = _interopRequireDefault(_fs); + +var _path = require("path"); + +var _path2 = _interopRequireDefault(_path); + +var _loaderUtils = require("loader-utils"); + +var _loaderUtils2 = _interopRequireDefault(_loaderUtils); + +var _grayMatter = require("gray-matter"); + +var _grayMatter2 = _interopRequireDefault(_grayMatter); + +var _pathToUri = require("../_utils/path-to-uri"); + +var _pathToUri2 = _interopRequireDefault(_pathToUri); + +var _urlify = require("../_utils/urlify"); + +var _urlify2 = _interopRequireDefault(_urlify); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// Use the path of the loader directory to avoid conflicts on the loaderContext +var NS = _fs2.default.realpathSync(__dirname); + +var loader = function loader(input) { + var _this = this; + + var webpackInstance = this; + + loader.getCollection = function () { + return _this[NS]; + }; + + var options = _extends({}, webpackInstance.options.phenomic, _loaderUtils2.default.getOptions(webpackInstance)); + + // removed + if (options.feeds) { + throw new Error("Phenomic loader `feed` option has been changed since 0.17.0. " + "The changelog was not mentionning this breaking change. " + "Sorry about that. " + "Please visit https://phenomic.io/docs/usage/feeds/ to know how to " + "migrate (spoiler: it's easy). "); + } + + var context = options.context || webpackInstance.options.context; + var plugins = options.plugins || require("../loader-preset-markdown").default; + + var relativePath = _path2.default.relative(context, webpackInstance.resourcePath); + + var frontMatter = (0, _grayMatter2.default)(input); + var pluginsResult = plugins.reduce(function (result, plugin) { + return plugin({ + frontMatter: frontMatter, + result: result, + options: options + }); + }, {}); + + var tmpUrl = (0, _urlify2.default)(pluginsResult.head && pluginsResult.head.route + // custom route + ? pluginsResult.head.route + // default route + : relativePath); + tmpUrl = tmpUrl.substring(0, 1) === "/" ? tmpUrl.slice(1) : tmpUrl; + + var url = (0, _urlify2.default)(tmpUrl); + var resourceUrl = (0, _urlify2.default)(tmpUrl, true); + var contentHash = _loaderUtils2.default.getHashDigest(input); + var dataUrl = resourceUrl + "." + contentHash + ".json"; + + var metadata = { + __filename: (0, _pathToUri2.default)(relativePath), + __url: (0, _pathToUri2.default)("/", url), + __resourceUrl: (0, _pathToUri2.default)("/", resourceUrl), + __dataUrl: (0, _pathToUri2.default)("/", dataUrl) + }; + + var result = _extends({}, pluginsResult, metadata); + + webpackInstance.emitFile(dataUrl, JSON.stringify(result)); + + if (typeof webpackInstance[NS] !== "function") { + throw new Error("You are using phenomic loader without the corresponding plugin. " + "This plugin should be added automatically by Phenomic, so if you are " + "facing this issue, you are probably playing with the fire. " + "To get more information, you can reach us on our community chat. " + "https://phenomic.io/"); + } + webpackInstance[NS](result); + + return "module.exports = " + JSON.stringify((0, _pathToUri2.default)("/", dataUrl)); +}; + +module.exports = loader; \ No newline at end of file diff --git a/lib/loader/minify.js b/lib/loader/minify.js new file mode 100644 index 000000000..817f9e37c --- /dev/null +++ b/lib/loader/minify.js @@ -0,0 +1,24 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +exports.default = function (collection) { + if (!Array.isArray(collection)) { + throw new Error("minify expect a valid collection instead of " + (typeof collection === "undefined" ? "undefined" : _typeof(collection))); + } + + return collection.map(function (item) { + return _extends({}, item.head, { + __filename: item.__filename, + __url: item.__url, + __resourceUrl: item.__resourceUrl, + __dataUrl: item.__dataUrl + }); + }); +}; \ No newline at end of file diff --git a/lib/loader/plugin.js b/lib/loader/plugin.js new file mode 100644 index 000000000..93fbdb3bb --- /dev/null +++ b/lib/loader/plugin.js @@ -0,0 +1,50 @@ +"use strict"; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _fs = require("fs"); + +var _fs2 = _interopRequireDefault(_fs); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// import { RawSource } from "webpack-sources" + +// Use the path of the plugin/loader directory to avoid conflicts on the +// loaderContext +var NS = _fs2.default.realpathSync(__dirname); + +function PhenomicLoaderWebpackPlugin() {} + +// Phenomic collection cache: it avoid to have to re-read content files +// between client and static build! +// We can do without this (eg: emitting a json + read the json later), +// but this will be an issue to consider for big websites. +PhenomicLoaderWebpackPlugin.collection = []; + +PhenomicLoaderWebpackPlugin.prototype.apply = function (compiler) { + compiler.plugin("compilation", function (compilation /* , params */) { + compilation.plugin("normal-module-loader", function (loaderContext, module) { + loaderContext[NS] = function (loaderResult) { + return module.meta[NS] = loaderResult; + }; + }); + + compilation.plugin("additional-assets", function (callback) { + var results = compilation.modules.map(function (module) { + return module && module.meta && module.meta[NS]; + }).filter(function (result) { + return result && (typeof result === "undefined" ? "undefined" : _typeof(result)) === "object"; + }); + + PhenomicLoaderWebpackPlugin.collection = results; + // const collection = JSON.stringify(results, null, 2) + // compilation.assets["phenomic.collection.json"] = + // new RawSource(collection) + + callback(); + }); + }); +}; + +module.exports = PhenomicLoaderWebpackPlugin; \ No newline at end of file diff --git a/lib/redux/createStore/index.js b/lib/redux/createStore/index.js new file mode 100644 index 000000000..c0449fabf --- /dev/null +++ b/lib/redux/createStore/index.js @@ -0,0 +1,26 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.default = function () { + var reducer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var initialState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var extraMiddlewares = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + var extraStoreEnhancers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + + var finalCreateStore = _redux.compose.apply(undefined, [_redux.applyMiddleware.apply(undefined, [_promise2.default].concat(_toConsumableArray(extraMiddlewares)))].concat(_toConsumableArray(extraStoreEnhancers)))(_redux.createStore); + + return finalCreateStore(reducer, initialState); +}; + +var _redux = require("redux"); + +var _promise = require("../middlewares/promise"); + +var _promise2 = _interopRequireDefault(_promise); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } \ No newline at end of file diff --git a/lib/redux/middlewares/promise.js b/lib/redux/middlewares/promise.js new file mode 100644 index 000000000..754a2c785 --- /dev/null +++ b/lib/redux/middlewares/promise.js @@ -0,0 +1,41 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +exports.default = promiseMiddleware; + +function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } + +function promiseMiddleware() { + return function (next) { + return function (action) { + var promise = action.promise, + types = action.types, + rest = _objectWithoutProperties(action, ["promise", "types"]); + + if (!promise) { + return next(action); + } else if (!promise.then) { + throw new Error("promiseMiddleware expects a promise object that implements then()"); + } + + var _types = _slicedToArray(types, 3), + REQUEST = _types[0], + SUCCESS = _types[1], + FAILURE = _types[2]; + + next(_extends({}, rest, { type: REQUEST })); + return promise.then(function (response) { + return next(_extends({}, rest, { response: response, type: SUCCESS })); + }, function (response) { + return next(_extends({}, rest, { response: response, type: FAILURE })); + }); + }; + }; +} \ No newline at end of file diff --git a/lib/redux/modules/README.md b/lib/redux/modules/README.md new file mode 100644 index 000000000..d4728bb2b --- /dev/null +++ b/lib/redux/modules/README.md @@ -0,0 +1 @@ +https://github.com/erikras/ducks-modular-redux diff --git a/lib/redux/modules/index.js b/lib/redux/modules/index.js new file mode 100644 index 000000000..65be3da48 --- /dev/null +++ b/lib/redux/modules/index.js @@ -0,0 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.pages = undefined; + +var _pages2 = require("./pages"); + +var _pages3 = _interopRequireDefault(_pages2); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.pages = _pages3.default; \ No newline at end of file diff --git a/lib/redux/modules/pages.js b/lib/redux/modules/pages.js new file mode 100644 index 000000000..077db4bb0 --- /dev/null +++ b/lib/redux/modules/pages.js @@ -0,0 +1,100 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ERROR = exports.FORGET = exports.SET_TYPE = exports.SET = exports.GET = exports.NOOP = undefined; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +exports.default = reducer; +exports.get = get; +exports.refresh = refresh; +exports.setNotFound = setNotFound; + +var _simpleJsonFetch = require("simple-json-fetch"); + +var _simpleJsonFetch2 = _interopRequireDefault(_simpleJsonFetch); + +var _pathToUri = require("../../_utils/path-to-uri"); + +var _pathToUri2 = _interopRequireDefault(_pathToUri); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +var NOOP = exports.NOOP = "phenomic/pages/NOOP"; +var GET = exports.GET = "phenomic/pages/GET"; +var SET = exports.SET = "phenomic/pages/SET"; +var SET_TYPE = exports.SET_TYPE = "phenomic/pages/SET_TYPE"; +var FORGET = exports.FORGET = "phenomic/pages/FORGET"; +var ERROR = exports.ERROR = "phenomic/pages/ERROR"; + +// redux reducer +function reducer() { + var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var action = arguments[1]; + + + switch (action.type) { + case GET: + return _extends({}, state, _defineProperty({}, action.page, { + isLoading: true + })); + + case SET: + { + var json = action.response.json; + + return _extends({}, state, _defineProperty({}, action.page, _extends({}, json, { + type: json.head ? json.head.layout || json.head.type : undefined + }))); + } + + case FORGET: + return _extends({}, state, _defineProperty({}, action.page, undefined)); + + case ERROR: + return _extends({}, state, _defineProperty({}, action.page, action.response ? action.response.status ? { + error: action.response.status, + errorText: action.response.statusText + } : { + error: "Unexpected Error", + errorText: action.response.message || action.response.error && action.response.error.message || + // here we are just in a deseperate case + "Seriously, this is weird. Please report this page." + } : + // no response, it's certainly a 404 + { + error: 404 + })); + + default: + return state; + } +} + +// redux actions +function get(page, url) { + return { + types: [GET, SET, ERROR], + page: page, + promise: (0, _simpleJsonFetch2.default)((0, _pathToUri2.default)(process.env.PHENOMIC_USER_PATHNAME, url)) + }; +} + +function refresh(page, url) { + return { + types: [NOOP, SET, ERROR], + page: page, + promise: (0, _simpleJsonFetch2.default)((0, _pathToUri2.default)(process.env.PHENOMIC_USER_PATHNAME, url)) + }; +} + +function setNotFound(page) { + return { + type: ERROR, + page: page + }; +} \ No newline at end of file diff --git a/lib/static/index.js b/lib/static/index.js new file mode 100644 index 000000000..81b97a5c9 --- /dev/null +++ b/lib/static/index.js @@ -0,0 +1,115 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +// eslint-disable-next-line import/no-namespace + + +exports.setPageData = setPageData; +exports.forgetPageData = forgetPageData; +exports.writeHTMLFile = writeHTMLFile; +exports.writeAllHTMLFiles = writeAllHTMLFiles; + +var _path = require("path"); + +var _path2 = _interopRequireDefault(_path); + +var _fsPromise = require("fs-promise"); + +var _fsPromise2 = _interopRequireDefault(_fsPromise); + +var _urlify = require("../_utils/urlify"); + +var _urlify2 = _interopRequireDefault(_urlify); + +var _pages = require("../redux/modules/pages"); + +var pagesActions = _interopRequireWildcard(_pages); + +var _routesToUrls = require("./routes-to-urls"); + +var _routesToUrls2 = _interopRequireDefault(_routesToUrls); + +var _urlAsHtml = require("./url-as-html"); + +var _urlAsHtml2 = _interopRequireDefault(_urlAsHtml); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +if (pagesActions.SET === undefined) { + throw new Error("pages SET action is undefined"); +} +if (pagesActions.FORGET === undefined) { + throw new Error("pages FORGET action is undefined"); +} + +function setPageData(url, collection, store) { + var json = collection.find(function (item) { + return item.__url === url; + }); + if (json) { + // prepare page data + store.dispatch({ + type: pagesActions.SET, + page: url, + response: { json: json } + }); + } +} + +function forgetPageData(url, store) { + // forget page data to avoid having all pages data in all pages + store.dispatch({ + type: pagesActions.FORGET, + page: url + }); +} + +function writeHTMLFile(filename, html) { + return _fsPromise2.default.mkdirs(_path2.default.dirname(filename)).then(function () { + return Promise.all([_fsPromise2.default.writeFile(filename, html)]); + }).then(function () { + return filename; + }); +} + +function writeAllHTMLFiles(options, testing) { + var routes = options.routes, + collection = options.collection, + destination = options.destination, + store = options.store, + Html = options.Html, + setPageData = options.setPageData, + forgetPageData = options.forgetPageData, + writeHTMLFile = options.writeHTMLFile; + + var urls = (0, _routesToUrls2.default)(routes, collection); + + // create all html files + return Promise.all(urls.map(function (url) { + var item = collection.find(function (item) { + return item.__url === url; + }); + var filename = decodeURIComponent(item ? _path2.default.join(destination, item.__resourceUrl) : _path2.default.join(destination, (0, _urlify2.default)(url, true))); + setPageData(url, collection, store); + return (0, _urlAsHtml2.default)(url, options, Html, testing).then(function (html) { + return writeHTMLFile(filename, html); + }).then(function (filename) { + forgetPageData(url, store); + return filename; + }); + })); +} + +exports.default = function (options, testing) { + return writeAllHTMLFiles(_extends({}, options, { + setPageData: setPageData, + forgetPageData: forgetPageData, + writeHTMLFile: writeHTMLFile + }), testing); +}; \ No newline at end of file diff --git a/lib/static/routes-to-urls/index.js b/lib/static/routes-to-urls/index.js new file mode 100644 index 000000000..5536f2fb8 --- /dev/null +++ b/lib/static/routes-to-urls/index.js @@ -0,0 +1,169 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _reactRouter = require("react-router"); + +var _chalk = require("chalk"); + +var _chalk2 = _interopRequireDefault(_chalk); + +var _urlJoin = require("url-join"); + +var _urlJoin2 = _interopRequireDefault(_urlJoin); + +var _arrayUnique = require("../../_utils/array-unique"); + +var _arrayUnique2 = _interopRequireDefault(_arrayUnique); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +var defaultConsole = console.log; + +var flattenRoute = function flattenRoute(route) { + // @todo remove the default route.path, user should use IndexRoute instead? + var routesUrls = route.path ? [route.path] : []; + + if (route.indexRoute) { + routesUrls.push(route.path); + } + if (route.childRoutes) { + var root = route.path; + route.childRoutes.forEach(function (route) { + routesUrls = [].concat(_toConsumableArray(routesUrls), _toConsumableArray(flattenRoute(route).map(function (r) { + return root ? (0, _urlJoin2.default)(root, r) : r; + }))); + }); + } + + return routesUrls; +}; + +// only simple types will be accepted +// array | string | number +// other will be skipped +var paramsListFromCollection = function paramsListFromCollection(collection) { + var params = {}; + + collection.forEach(function (item) { + if (!item.head) { + return; + } + + Object.keys(item.head).forEach(function (key) { + // array + if (Array.isArray(item.head[key]) && item.head[key].length && (typeof item.head[key][0] === "string" || typeof item.head[key][0] === "number")) { + var k = key + // categories => category + .replace(/ies$/, "y") + // tags => tag + .replace(/s$/, ""); + + if (!params[k]) { + params[k] = []; + } + params[k] = [].concat(_toConsumableArray(params[k]), _toConsumableArray(item.head[key])); + } + + // string, number + if (typeof item.head[key] === "string" || typeof item.head[key] === "number") { + if (!params[key]) { + params[key] = []; + } + params[key].push(item.head[key]); + } + }); + }); + + params.splat = collection.map(function (item) { + return item.__url; + }); + + return params; +}; + +var createUrlsFromParamsReplacementInUrl = function createUrlsFromParamsReplacementInUrl(url, params, log) { + // don't compute anything if url doesn't seems to have dynamic parameters + // react-router url params are like ``:that`` (or splat *) + if (url.indexOf(":") === -1 && url.indexOf("*") === -1) { + return [url]; + } + + var urls = []; + + var possibleErrorEnd = "parameter for path \"" + url + "\""; + + var missingKeys = []; + var nonMissingKeys = []; + + Object.keys(params).forEach(function (paramName) { + params[paramName].forEach(function (paramValue) { + var urlParams = _defineProperty({}, paramName, paramValue); + try { + var hydratedUrl = (0, _reactRouter.formatPattern)(url, urlParams); + urls.push(hydratedUrl); + nonMissingKeys.push(paramName); + } catch (e) { + // log(paramName, e.message) + + var matches = e.message.match(/Missing \"(.*)\" parameter for path/); + if (matches && matches[1]) { + missingKeys.push(matches[1]); + } + + if (e.message.indexOf("Missing") === 0 && e.message.lastIndexOf(possibleErrorEnd) === e.message.length - possibleErrorEnd.length - 1) { + throw e; + } + } + }); + }); + + nonMissingKeys = (0, _arrayUnique2.default)(nonMissingKeys); + missingKeys = (0, _arrayUnique2.default)(missingKeys).filter(function (key) { + return nonMissingKeys.indexOf(key) === -1; + }); + if (missingKeys.length) { + log("⚠️ " + _chalk2.default.red("It looks like some parameters can't be mapped to create routes: ", missingKeys.map(function (key) { + return ":" + key; + }).join(", "))); + } + + // @todo improve the algorithm to avoid duplicates, + // we will probably get better perfs + + return (0, _arrayUnique2.default)(urls.sort()); +}; + +var hydrateRoutesUrls = function hydrateRoutesUrls(routesUrls, collection, log) { + var paramsList = paramsListFromCollection(collection); + + return routesUrls.reduce(function (acc, url) { + return [].concat(_toConsumableArray(acc), _toConsumableArray(createUrlsFromParamsReplacementInUrl(url, paramsList, log))); + }, []); +}; + +exports.default = function (routes, collection) { + var log = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : defaultConsole; + + var flattenedRoutes = (0, _arrayUnique2.default)((0, _reactRouter.createRoutes)(routes).reduce(function (acc, r) { + return [].concat(_toConsumableArray(acc), _toConsumableArray(flattenRoute(r))); + }, [])); + + if (flattenedRoutes.filter(function (url) { + return url.indexOf("*") > -1; + }).length > 1) { + throw new Error("Phenomic can only handle one splat (*) in react-router routes. \n" + "You must use only one splat. If you have a specific need, do not " + "hesitate to open an issue at " + "https://github.com/MoOx/phenomic/issues/new"); + } + + var normalizedRoutes = flattenedRoutes.map(function (route) { + return "/" + route.replace(/^\/+/, "").replace(/\/+$/, ""); + }); + + return hydrateRoutesUrls(normalizedRoutes, collection, log); +}; \ No newline at end of file diff --git a/lib/static/url-as-html.js b/lib/static/url-as-html.js new file mode 100644 index 000000000..1d0517153 --- /dev/null +++ b/lib/static/url-as-html.js @@ -0,0 +1,133 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +exports.default = function (url, options) { + var Html = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _Html2.default; + var baseUrl = options.baseUrl, + assetsFiles = options.assetsFiles, + routes = options.routes, + collection = options.collection, + metadata = options.metadata, + store = options.store; + + + var render = _server2.default[!options.clientScripts ? "renderToStaticMarkup" : "renderToString"]; + + return new Promise(function (resolve, reject) { + try { + (0, _reactRouter.match)({ + routes: routes, + location: url, + basename: baseUrl.pathname + }, function (error, redirectLocation, renderProps) { + if (error) { + return reject(error); + } else if (redirectLocation) { + // TODO add a redirect page à la "jekyll redirect plugin" + throw new Error("phenomic (static) doesn't handle redirection yet"); + } else if (!renderProps) { + throw new Error("phenomic (static) doesn't handle page not found yet. " + "You are not supposed so see this message because this code is " + "not supposed to be executed the way thing are, so this can " + "be a react-router issue. Check out opened issue to find a " + "workaround: https://github.com/MoOx/phenomic/issues"); + } + + var collectionMin = (0, _minify2.default)(collection); + + /* eslint-disable react/no-multi-comp */ + + var sheet = new _styledComponents.ServerStyleSheet(); + var renderBody = function renderBody() { + return render(_react2.default.createElement( + _styledComponents.StyleSheetManager, + { sheet: sheet.instance }, + _react2.default.createElement( + _ContextProvider2.default, + { + collection: collectionMin, + metadata: metadata + }, + _react2.default.createElement( + _reactRedux.Provider, + { store: store }, + _react2.default.createElement(_reactRouter.RouterContext, renderProps) + ) + ) + )); + }; + + var renderScript = function renderScript() { + if (options.clientScripts) { + var initialState = _extends({}, store.getState(), { + // only keep current page as others are not necessary + pages: _defineProperty({}, url, store.getState().pages[url]) + }); + var script = "window.__COLLECTION__ = " + (0, _serialize2.default)(collectionMin) + ";" + ("window.__INITIAL_STATE__ = " + (0, _serialize2.default)(initialState)); + + return _react2.default.createElement("script", { dangerouslySetInnerHTML: { __html: script } }); + } + + return null; + }; + + // write htmlString as html files + return resolve( + // render html document as simple html + "" + _server2.default.renderToStaticMarkup(_react2.default.createElement(Html, _extends({}, assetsFiles && { + css: assetsFiles.css ? assetsFiles.css.map(function (fileName) { + return (0, _pathToUri2.default)(baseUrl.pathname, fileName); + }) : [], + js: options.clientScripts && assetsFiles.js ? assetsFiles.js.map(function (fileName) { + return (0, _pathToUri2.default)(baseUrl.pathname, fileName); + }) : [] + }, { + renderBody: renderBody, + renderScript: renderScript, + sheet: sheet + })))); + }); + } catch (err) { + reject(err); + } + }); +}; + +var _react = require("react"); + +var _react2 = _interopRequireDefault(_react); + +var _server = require("react-dom/server"); + +var _server2 = _interopRequireDefault(_server); + +var _reactRouter = require("react-router"); + +var _reactRedux = require("react-redux"); + +var _Html = require("../components/Html"); + +var _Html2 = _interopRequireDefault(_Html); + +var _pathToUri = require("../_utils/path-to-uri"); + +var _pathToUri2 = _interopRequireDefault(_pathToUri); + +var _ContextProvider = require("../components/ContextProvider"); + +var _ContextProvider2 = _interopRequireDefault(_ContextProvider); + +var _serialize = require("../_utils/serialize"); + +var _serialize2 = _interopRequireDefault(_serialize); + +var _minify = require("../loader/minify"); + +var _minify2 = _interopRequireDefault(_minify); + +var _styledComponents = require("styled-components"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } \ No newline at end of file diff --git a/lib/stylelint-config-recommended/index.js b/lib/stylelint-config-recommended/index.js new file mode 100644 index 000000000..6139e2304 --- /dev/null +++ b/lib/stylelint-config-recommended/index.js @@ -0,0 +1,13 @@ +"use strict"; + +module.exports = { + // too many opinionated rules + // "extends": "stylelint-config-standard", + rules: { + // only prevent user errors + "string-no-newline": true, + "unit-no-unknown": true, + "property-no-unknown": true, + "declaration-block-no-duplicate-properties": [true, { "ignore": ["consecutive-duplicates-with-different-values"] }] + } +}; \ No newline at end of file diff --git a/package.json b/package.json index 2bd5daa04..5edaf949c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "phenomic", - "version": "0.21.1", + "version": "0.21.2-0", "description": "Modern website generator based on the React and Webpack ecosystem.", "keywords": [ "webpack", @@ -81,6 +81,7 @@ "sitemap": "^1.8.2", "source-map-support": "^0.4.0", "strip-markdown": "^0.3.1", + "styled-components": "^2.1.0", "url-join": "^1.1.0", "valid-url": "^1.0.9", "webpack-dev-middleware": "^1.10.1", @@ -144,7 +145,6 @@ "webpack": "^2.3.0" }, "scripts": { - "postinstall": "node npm/postinstall.js", "transpile": "babel --ignore __tests__ --copy-files src --out-dir lib", "prepublish": "rimraf lib && npm run transpile", "#lint:js:eslint": "https://github.com/eslint/eslint/issues/5679", diff --git a/src/components/Html/index.js b/src/components/Html/index.js index d3d9bf5ae..a7838c396 100644 --- a/src/components/Html/index.js +++ b/src/components/Html/index.js @@ -4,11 +4,14 @@ import React from "react" import { renderToString } from "react-dom/server" import Helmet from "react-helmet" +import { ServerStyleSheet } from "styled-components" + type Props = { css: Array, js: Array, renderBody: () => React$Element, renderScript: () => React$Element, + sheet: any } const Html = (props: Props) => { @@ -76,6 +79,7 @@ const Html = (props: Props) => { } body = body || props.renderBody() + const styledComponentsStyles = props.sheet.getStyleElement() // rewind html metas const head = Helmet.rewind() @@ -100,6 +104,7 @@ const Html = (props: Props) => { )) } + { styledComponentsStyles } { head.script.toComponent() } diff --git a/src/static/url-as-html.js b/src/static/url-as-html.js index a75d4986e..453a0ee85 100644 --- a/src/static/url-as-html.js +++ b/src/static/url-as-html.js @@ -11,6 +11,8 @@ import PhenomicContextProvider from "../components/ContextProvider" import serialize from "../_utils/serialize" import minifyCollection from "../loader/minify" +import { ServerStyleSheet, StyleSheetManager } from "styled-components" + export default function( url: string, options: PhenomicStaticConfig, @@ -64,15 +66,18 @@ export default function( /* eslint-disable react/no-multi-comp */ + const sheet = new ServerStyleSheet() const renderBody = () => render( - - - - - + + + + + + + ) const renderScript = () => { @@ -119,6 +124,7 @@ export default function( }, renderBody, renderScript, + sheet } ) ) diff --git a/yarn.lock b/yarn.lock index 9adbeedea..6598ec95e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1231,6 +1231,13 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.0.3: + version "5.0.6" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.6.tgz#2ea669f7eec0b6eda05b08f8b5ff661b28573588" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -1660,6 +1667,10 @@ crypto-browserify@^3.11.0: public-encrypt "^4.0.0" randombytes "^2.0.0" +css-color-keywords@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" + css-color-names@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.3.tgz#de0cef16f4d8aa8222a320d5b6d7e9bbada7b9f6" @@ -1673,6 +1684,14 @@ css-rule-stream@^1.1.0: ldjson-stream "^1.2.1" through2 "^0.6.3" +css-to-react-native@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.0.4.tgz#cf4cc407558b3474d4ba8be1a2cd3b6ce713101b" + dependencies: + css-color-keywords "^1.0.0" + fbjs "^0.8.5" + postcss-value-parser "^3.3.0" + css-tokenize@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/css-tokenize/-/css-tokenize-1.0.1.tgz#4625cb1eda21c143858b7f81d6803c1d26fc14be" @@ -2411,7 +2430,7 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" -fbjs@^0.8.1, fbjs@^0.8.4: +fbjs@^0.8.1, fbjs@^0.8.4, fbjs@^0.8.5, fbjs@^0.8.9: version "0.8.12" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04" dependencies: @@ -3251,6 +3270,10 @@ is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" +is-function@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" + is-generator-function@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.6.tgz#9e71653cd15fff341c79c4151460a131d31e9fc4" @@ -4066,7 +4089,7 @@ longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" dependencies: @@ -4265,13 +4288,6 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: dependencies: minimist "0.0.8" -mock-fs@^3.9.0: - version "3.12.1" - resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-3.12.1.tgz#ff27824cd6ab263a7eb05a115239d41d3631f5f8" - dependencies: - rewire "2.5.2" - semver "5.3.0" - mount-point@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mount-point/-/mount-point-1.2.0.tgz#f7712295e1ecd8df2e42ecbe23e07a3660807851" @@ -4844,7 +4860,7 @@ postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.1.1: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-value-parser@^3.1.1, postcss-value-parser@^3.2.3: +postcss-value-parser@^3.1.1, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" @@ -4911,6 +4927,13 @@ promise@^7.1.1: dependencies: asap "~2.0.3" +prop-types@^15.5.4: + version "15.5.10" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" + dependencies: + fbjs "^0.8.9" + loose-envify "^1.3.1" + property-information@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-3.1.0.tgz#1581bf8a445dfbfef759775a86700e8dda18b4a1" @@ -5430,10 +5453,6 @@ restore-cursor@^1.0.1: exit-hook "^1.0.0" onetime "^1.0.0" -rewire@2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/rewire/-/rewire-2.5.2.tgz#6427de7b7feefa7d36401507eb64a5385bc58dc7" - right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" @@ -5511,7 +5530,7 @@ scroll-behavior@^0.8.0: dom-helpers "^2.4.0" invariant "^2.2.1" -"semver@2 || 3 || 4 || 5", semver@5.3.0, semver@^5.3.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -5884,6 +5903,20 @@ style-search@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" +styled-components@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.1.0.tgz#425805fca7efa5880aad2171f986bfd8a2f0808f" + dependencies: + buffer "^5.0.3" + css-to-react-native "^2.0.3" + fbjs "^0.8.9" + hoist-non-react-statics "^1.2.0" + is-function "^1.0.1" + is-plain-object "^2.0.1" + prop-types "^15.5.4" + stylis "^3.0.19" + supports-color "^3.2.3" + stylehacks@^2.3.0: version "2.3.2" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-2.3.2.tgz#64c83e0438a68c9edf449e8c552a7d9ab6009b0b" @@ -5943,6 +5976,10 @@ stylelint@^7.2.0: svg-tags "^1.0.0" table "^4.0.1" +stylis@^3.0.19: + version "3.1.9" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.1.9.tgz#638370451f980437f57c59e58d2e296be29fafb7" + sugarss@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-0.2.0.tgz#ac34237563327c6ff897b64742bf6aec190ad39e"