Last active
April 5, 2017 18:31
-
-
Save dfreeman/c5fdb07a68868fce84835dcea0b60026 to your computer and use it in GitHub Desktop.
flow
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Ember from 'ember'; | |
| import Scope from 'twiddle/cf/scope'; | |
| import buildModule from 'twiddle/cf/build-module'; | |
| const { | |
| Controller, | |
| computed, | |
| } = Ember; | |
| export default Controller.extend({ | |
| context: computed(function() { | |
| return { | |
| items: [ | |
| { label: 'a' }, | |
| { label: 'b' }, | |
| { label: 'c' }, | |
| ] | |
| }; | |
| }), | |
| rootScope: computed('context', function() { | |
| const context = this.get('context'); | |
| return new Scope({ context }); | |
| }), | |
| viewModule: buildModule({ | |
| module: 'each', | |
| config: { | |
| items: { bind: 'context.items' }, | |
| yield: 'item', | |
| eachModule: { | |
| module: 'text', | |
| config: { | |
| content: [{ bind: 'item.label' }] | |
| } | |
| } | |
| } | |
| }), | |
| editModule: buildModule({ | |
| module: 'each', | |
| config: { | |
| items: { bind: 'context.items' }, | |
| yield: 'item', | |
| eachModule: { | |
| module: 'input', | |
| config: { | |
| value: { bind: 'item.label' }, | |
| } | |
| } | |
| } | |
| }), | |
| // Nasty hacks below here to keep the stringified context up to date | |
| init() { | |
| this._super(...arguments); | |
| const context = this.get('context'); | |
| let previous = JSON.stringify(context); | |
| this.interval = setInterval(() => { | |
| const current = JSON.stringify(context); | |
| if (previous !== current) { | |
| previous = current; | |
| this.notifyPropertyChange('jsonContext'); | |
| } | |
| }, 20); | |
| }, | |
| willDestroy() { | |
| clearInterval(this.interval); | |
| this._super(...arguments); | |
| }, | |
| jsonContext: computed(function() { | |
| return JSON.stringify(this.get('context'), null, 2); | |
| }), | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Ember from 'ember'; | |
| import { read, write } from './manipulators'; | |
| export default class Binding { | |
| constructor(path) { | |
| const firstDot = path.indexOf('.'); | |
| if (firstDot === -1) { | |
| this.scopeKey = path; | |
| this.path = null; | |
| } else { | |
| this.scopeKey = path.substring(0, firstDot); | |
| this.path = path.substring(firstDot + 1); | |
| } | |
| } | |
| read(options) { | |
| const value = options.scope.lookup(this.scopeKey); | |
| if (this.path) { | |
| return read(value, this.path, options); | |
| } else { | |
| return value; | |
| } | |
| } | |
| write(value, options) { | |
| if (!this.path) throw new Error(`Don't do that.`); | |
| const target = options.scope.lookup(this.scopeKey); | |
| write(target, this.path, value, options); | |
| } | |
| validate(options) { | |
| // TODO ¯\_(ツ)_/¯ | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Binding from 'twiddle/cf/binding'; | |
| import { transform, rule, simple, subtree } from 'botanist'; | |
| export default transform([ | |
| rule({ | |
| bind: simple('name') | |
| }, ({ name }) => { | |
| return new Binding(name); | |
| }), | |
| rule({ | |
| module: simple('moduleName'), | |
| config: subtree('moduleConfig') | |
| }, ({ moduleName, moduleConfig }) => { | |
| return { | |
| componentPath: `cf-modules/${moduleName}`, | |
| config: moduleConfig | |
| }; | |
| }) | |
| ]); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Ember from 'ember'; | |
| import { read, write } from './manipulators'; | |
| const { | |
| computed, | |
| addObserver, | |
| propertyDidChange, | |
| } = Ember; | |
| export function data(key, options = {}) { | |
| return computed(key, 'scope', { | |
| get() { | |
| const scope = this.get('scope'); | |
| const onResolve = (object, property) => registerDependentProperty(object, property, this, key); | |
| return read(this, key, { ...options, scope, onResolve }); | |
| }, | |
| set(localKey, value) { | |
| const scope = this.get('scope'); | |
| write(this, key, value, { scope }); | |
| return value; | |
| } | |
| }); | |
| } | |
| const SOURCES = window.SOURCES = new WeakMap(); | |
| function registerDependentProperty(sourceObj, sourceKey, dependentObj, dependentKey) { | |
| const dependentObjects = dependentObjectsFor(sourceObj, sourceKey); | |
| let dependentKeys = dependentObjects.get(dependentObj); | |
| if (!dependentKeys) { | |
| dependentKeys = new Set(); | |
| dependentObjects.set(dependentObj, dependentKeys); | |
| addObserver(sourceObj, sourceKey, dependentObj, () => { | |
| for (const key of dependentKeys) { | |
| propertyDidChange(dependentObj, key); | |
| } | |
| }); | |
| } | |
| dependentKeys.add(dependentKey); | |
| } | |
| function dependentObjectsFor(sourceObj, sourceKey) { | |
| let keysForSource = SOURCES.get(sourceObj); | |
| if (!keysForSource) { | |
| keysForSource = new Map(); | |
| SOURCES.set(sourceObj, keysForSource); | |
| } | |
| let dependentObjects = keysForSource.get(sourceKey); | |
| if (!dependentObjects) { | |
| dependentObjects = new WeakMap(); | |
| keysForSource.set(sourceKey, dependentObjects); | |
| } | |
| return dependentObjects; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Ember from 'ember'; | |
| import Binding from './binding'; | |
| const { | |
| get, | |
| set, | |
| propertyDidChange, | |
| } = Ember; | |
| export function read(obj, path, options = {}) { | |
| const { scope, onResolve } = options; | |
| const segments = path.split('.'); | |
| let parent = null; | |
| let current = obj; | |
| for (const segment of segments) { | |
| parent = current; | |
| current = get(current, segment); | |
| if (current instanceof Binding) { | |
| current = current.read(options); | |
| } | |
| if (current === undefined && 'default' in options) { | |
| return options.default; | |
| } | |
| // TODO this might be a terrible idea | |
| if (Array.isArray(current)) { | |
| current = current.map((item) => { | |
| return item instanceof Binding ? item.read(options) : item; | |
| }); | |
| } | |
| } | |
| const last = segments[segments.length - 1]; | |
| if (onResolve && !(get(parent, last) instanceof Binding)) { | |
| onResolve(parent, last); | |
| } | |
| return current; | |
| } | |
| export function write(obj, path, value, options = {}) { | |
| const lastDot = path.lastIndexOf('.'); | |
| const readPath = path.substring(0, lastDot); | |
| const writeKey = path.substring(lastDot + 1); | |
| let parent = obj; | |
| if (readPath) { | |
| parent = read(obj, readPath, options); | |
| } | |
| const existingValue = get(parent, writeKey); | |
| if (existingValue instanceof Binding) { | |
| existingValue.write(value, options); | |
| propertyDidChange(parent, writeKey); | |
| } else { | |
| set(parent, writeKey, value); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| export default class Scope { | |
| constructor(bound = Object.create(null)) { | |
| this.bound = bound; | |
| } | |
| lookup(key) { | |
| if (key in this.bound) { | |
| return this.bound[key]; | |
| } else { | |
| throw new Error(`Unbound identifier '${key}'`); | |
| } | |
| } | |
| extend(values) { | |
| const newBound = Object.create(this.bound); | |
| Object.assign(newBound, values); | |
| return new Scope(newBound); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Ember from 'ember'; | |
| import { data } from 'twiddle/cf/computed-macros'; | |
| const { | |
| Component, | |
| computed, | |
| } = Ember; | |
| export default Component.extend({ | |
| items: data('config.items'), | |
| yield: data('config.yield'), | |
| childModule: data('config.eachModule'), | |
| itemScopes: computed('items.[]', 'scope', function() { | |
| const scope = this.get('scope'); | |
| const key = this.get('yield'); | |
| return this.get('items').map(item => scope.extend({ [key]: item })); | |
| }) | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Ember from 'ember'; | |
| import { data } from 'twiddle/cf/computed-macros'; | |
| const { | |
| Component, | |
| computed, | |
| } = Ember; | |
| export default Component.extend({ | |
| value: data('config.value'), | |
| type: data('config.type', { default: 'text' }), | |
| placeholder: data('config.placeholder') | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Ember from 'ember'; | |
| import { data } from 'twiddle/cf/computed-macros'; | |
| const { | |
| Component, | |
| computed, | |
| } = Ember; | |
| export default Component.extend({ | |
| content: data('config.content'), | |
| text: computed('content', function() { | |
| return this.get('content').join(''); | |
| }) | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Ember from 'ember'; | |
| export default Ember.Component.extend({ | |
| willRender() { | |
| console.time(`Module: ${this.get('module.componentPath')}`); | |
| this._super(...arguments); | |
| }, | |
| didRender() { | |
| this._super(...arguments); | |
| console.timeEnd(`Module: ${this.get('module.componentPath')}`); | |
| } | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'twiddle/vendor'; | |
| import Ember from 'ember'; | |
| import config from './config/environment'; | |
| const Router = Ember.Router.extend({ | |
| location: 'none', | |
| rootURL: config.rootURL | |
| }); | |
| Router.map(function() { | |
| }); | |
| export default Router; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "version": "0.12.1", | |
| "EmberENV": { | |
| "FEATURES": {} | |
| }, | |
| "options": { | |
| "use_pods": false, | |
| "enable-testing": false | |
| }, | |
| "dependencies": { | |
| "jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js", | |
| "ember": "2.12.0", | |
| "ember-template-compiler": "2.12.0", | |
| "ember-testing": "2.12.0" | |
| }, | |
| "addons": { | |
| "ember-truth-helpers": "*" | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| define('botanist', ['exports'], function(exports) { | |
| var botanist = | |
| /******/ (function(modules) { // webpackBootstrap | |
| /******/ // The module cache | |
| /******/ var installedModules = {}; | |
| /******/ | |
| /******/ // The require function | |
| /******/ function __webpack_require__(moduleId) { | |
| /******/ | |
| /******/ // Check if module is in cache | |
| /******/ if(installedModules[moduleId]) | |
| /******/ return installedModules[moduleId].exports; | |
| /******/ | |
| /******/ // Create a new module (and put it into the cache) | |
| /******/ var module = installedModules[moduleId] = { | |
| /******/ i: moduleId, | |
| /******/ l: false, | |
| /******/ exports: {} | |
| /******/ }; | |
| /******/ | |
| /******/ // Execute the module function | |
| /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); | |
| /******/ | |
| /******/ // Flag the module as loaded | |
| /******/ module.l = true; | |
| /******/ | |
| /******/ // Return the exports of the module | |
| /******/ return module.exports; | |
| /******/ } | |
| /******/ | |
| /******/ | |
| /******/ // expose the modules object (__webpack_modules__) | |
| /******/ __webpack_require__.m = modules; | |
| /******/ | |
| /******/ // expose the module cache | |
| /******/ __webpack_require__.c = installedModules; | |
| /******/ | |
| /******/ // identity function for calling harmony imports with the correct context | |
| /******/ __webpack_require__.i = function(value) { return value; }; | |
| /******/ | |
| /******/ // define getter function for harmony exports | |
| /******/ __webpack_require__.d = function(exports, name, getter) { | |
| /******/ if(!__webpack_require__.o(exports, name)) { | |
| /******/ Object.defineProperty(exports, name, { | |
| /******/ configurable: false, | |
| /******/ enumerable: true, | |
| /******/ get: getter | |
| /******/ }); | |
| /******/ } | |
| /******/ }; | |
| /******/ | |
| /******/ // getDefaultExport function for compatibility with non-harmony modules | |
| /******/ __webpack_require__.n = function(module) { | |
| /******/ var getter = module && module.__esModule ? | |
| /******/ function getDefault() { return module['default']; } : | |
| /******/ function getModuleExports() { return module; }; | |
| /******/ __webpack_require__.d(getter, 'a', getter); | |
| /******/ return getter; | |
| /******/ }; | |
| /******/ | |
| /******/ // Object.prototype.hasOwnProperty.call | |
| /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; | |
| /******/ | |
| /******/ // __webpack_public_path__ | |
| /******/ __webpack_require__.p = ""; | |
| /******/ | |
| /******/ // Load entry module and return exports | |
| /******/ return __webpack_require__(__webpack_require__.s = 5); | |
| /******/ }) | |
| /************************************************************************/ | |
| /******/ ([ | |
| /* 0 */ | |
| /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
| "use strict"; | |
| /* harmony export (immutable) */ __webpack_exports__["d"] = isSimple; | |
| /* harmony export (immutable) */ __webpack_exports__["b"] = isArray; | |
| /* harmony export (immutable) */ __webpack_exports__["a"] = isObject; | |
| /* harmony export (immutable) */ __webpack_exports__["c"] = isFunction; | |
| /** | |
| * Indicates whether a given value is "simple", i.e. it should be matched by the | |
| * simple() binding directive, and will not have recursive rules applied against it. | |
| */ | |
| function isSimple(value) { | |
| return !isArray(value) && !isObject(value) && !isFunction(value); | |
| } | |
| /** | |
| * Indicates whether the given value is an array. | |
| */ | |
| function isArray(value) { | |
| return Array.isArray(value); | |
| } | |
| /** | |
| * Indicates whether the given value is a POJO, typically created via an object literal. | |
| */ | |
| function isObject(value) { | |
| if (!value || typeof value !== 'object') return false; | |
| let proto = Object.getPrototypeOf(value); | |
| return proto === null || proto === Object.prototype; | |
| } | |
| /** | |
| * Indicates whether the given value is a function | |
| */ | |
| function isFunction(value) { | |
| return typeof value === 'function'; | |
| } | |
| /***/ }), | |
| /* 1 */ | |
| /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
| "use strict"; | |
| /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils__ = __webpack_require__(0); | |
| /* harmony export (immutable) */ __webpack_exports__["a"] = simple; | |
| /* harmony export (immutable) */ __webpack_exports__["e"] = choice; | |
| /* harmony export (immutable) */ __webpack_exports__["b"] = match; | |
| /* harmony export (immutable) */ __webpack_exports__["c"] = sequence; | |
| /* harmony export (immutable) */ __webpack_exports__["d"] = subtree; | |
| /** | |
| * Matches any simple value and binds it to the given name in the rule handler. | |
| * The following would match any object with only a `key` property whose value | |
| * was a string, number, boolean, etc. It would also match non-POJO objects, such | |
| * as Ember class instances. | |
| * | |
| * @rule({ key: simple('name') }) | |
| * shoutName({ name }) { | |
| * return { key: name.toUpperCase() }; | |
| * } | |
| * | |
| * Input: { foo: { key: 'one' }, bar: { key: 'two' } } | |
| * Output: { foo: { key: 'ONE' }, bar: { key: 'TWO' } }} | |
| */ | |
| function simple(name) { | |
| return binder(name, node => __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__utils__["d" /* isSimple */])(node) ? node : NO_MATCH); | |
| } | |
| /** | |
| * Matches any simple value that is in the given list. | |
| * | |
| * @rule({ measure: simple('measure'), unit: choice(['in', 'ft'], 'unit') }) | |
| * convertToMetric({ measure, unit }) { | |
| * let factor = unit === 'ft' ? 30.48 : 2.54; | |
| * return { measure: measure * factor, unit: 'cm' }; | |
| * } | |
| * | |
| * Input: { measure: 3, unit: 'in' } | |
| * Output: { measure: 7.62, unit: 'cm' } | |
| * | |
| * Input: { measure: 3, unit: 'cm' } | |
| * Output: { measure: 3, unit: 'cm' } | |
| */ | |
| function choice(options, name) { | |
| return binder(name, node => __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__utils__["d" /* isSimple */])(node) && options.indexOf(node) !== -1 ? node : NO_MATCH); | |
| } | |
| /** | |
| * Matches any string that matches the given regex, binding the results of that | |
| * regex's execution to the given name. | |
| * | |
| * @rule({ key: match(/^(foo|bar)(baz|qux)$/, 'keyParts') }) | |
| * shoutName({ keyParts: [first, second, everything] }) { | |
| * return { first, second, everything }; | |
| * } | |
| * | |
| * Input: { foo: { key: 'fooqux' } } | |
| * Output: { foo: { first: 'foo', second: 'qux', everything: 'fooqux' } } | |
| */ | |
| function match(regex, name) { | |
| return binder(name, (node) => { | |
| let matchResult; | |
| if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__utils__["d" /* isSimple */])(node) && (matchResult = regex.exec(node))) { | |
| return matchResult.slice(1).concat(matchResult[0]); | |
| } else { | |
| return NO_MATCH; | |
| } | |
| }); | |
| } | |
| /** | |
| * Matches an array of simple values, binding it to the given name. | |
| * | |
| * @rule({ numbers: sequence('numbers') }) | |
| * increment({ numbers }) { | |
| * return numbers.map(n => n + 1); | |
| * } | |
| * | |
| * Input: { numbers: [1, 2, 3] } | |
| * Output: { numbers: [2, 3, 4] } | |
| */ | |
| function sequence(name) { | |
| return binder(name, node => __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__utils__["b" /* isArray */])(node) && node.every(__WEBPACK_IMPORTED_MODULE_0__utils__["d" /* isSimple */]) ? node : NO_MATCH); | |
| } | |
| /** | |
| * Matches any subtree, including complex values, binding it to the given name. Note | |
| * that any such rule will be applied AFTER rules that match the subtree. | |
| * | |
| * @rule({ key: simple('key') }) | |
| * shout({ key }) { | |
| * return { key: key.toUpperCase() }; | |
| * } | |
| * | |
| * @rule({ root: subtree('root') }) | |
| * values({ root }) { | |
| * return { root: Object.values(root) }; | |
| * } | |
| * | |
| * Input: { root: { key: 'hello' } } | |
| * Output: { root: ['HELLO'] } | |
| */ | |
| function subtree(name) { | |
| return binder(name, node => node); | |
| } | |
| const NO_MATCH = Object.freeze({}); | |
| function binder(name, test) { | |
| return (node, context) => { | |
| let result = test(node); | |
| if (result !== NO_MATCH) { | |
| return !name || context.bind(name, result); | |
| } | |
| }; | |
| } | |
| /***/ }), | |
| /* 2 */ | |
| /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
| "use strict"; | |
| /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__context__ = __webpack_require__(4); | |
| /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__compile_matcher__ = __webpack_require__(3); | |
| /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils__ = __webpack_require__(0); | |
| /* harmony export (immutable) */ __webpack_exports__["b"] = applyRules; | |
| /* harmony export (immutable) */ __webpack_exports__["a"] = extractRules; | |
| /* harmony export (immutable) */ __webpack_exports__["c"] = compileRule; | |
| /* harmony export (immutable) */ __webpack_exports__["d"] = memoizeRule; | |
| /** | |
| * Recursively applies the given set of rules to the given AST. | |
| */ | |
| function applyRules(ast, rules, options) { | |
| if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["a" /* isObject */])(ast)) { | |
| let result = Object.create(null); | |
| Object.keys(ast).forEach((key) => { | |
| result[key] = applyRules(ast[key], rules, options); | |
| }); | |
| return applyMatchingRule(result, rules, options); | |
| } else if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["b" /* isArray */])(ast)) { | |
| let result = ast.map(node => applyRules(node, rules, options)); | |
| return applyMatchingRule(result, rules, options); | |
| } else { | |
| return ast; | |
| } | |
| } | |
| /** | |
| * Given an object or array containing a combination of rule definitions and other such | |
| * arrays/objects, returns a flattened array of canonicalized rules. | |
| */ | |
| function extractRules(object) { | |
| let rules = rulesFromObject(object); | |
| if (!__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["b" /* isArray */])(rules)) return [object]; | |
| let flattened = []; | |
| for (let i = 0, len = rules.length; i < len; i++) { | |
| flattened.push(...extractRules(rules[i])); | |
| } | |
| return flattened; | |
| } | |
| /** | |
| * Given a matcher spec and a transform function, immediately compiles the pair into a rule. | |
| */ | |
| function compileRule(spec, transform) { | |
| let match = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__compile_matcher__["a" /* default */])(spec); | |
| return { match, transform }; | |
| } | |
| /** | |
| * Given an object, a matcher spec, and the name of a method on that object representing an | |
| * AST transform, compiles and stores the matcher on that object for later retrieval. | |
| */ | |
| function memoizeRule(object, transformKey, spec) { | |
| if (!(RULES in object)) object[RULES] = []; | |
| let match = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__compile_matcher__["a" /* default */])(spec); | |
| object[RULES].push({ match, transformKey }); | |
| } | |
| // Key where rule annotations are stashed on a decorated object. We don't use a Symbol because | |
| // they're never exposed in for-in loops, so it makes working with e.g. Ember objects unwieldy. | |
| const RULES = '__botanist-rules'; | |
| // Find a rule matching the given node and apply it, or just return the node | |
| function applyMatchingRule(node, rules, options) { | |
| let context = new __WEBPACK_IMPORTED_MODULE_0__context__["a" /* default */](); | |
| for (let i = 0, len = rules.length; i < len; i++) { | |
| let rule = rules[i]; | |
| if (rule.match(node, context)) { | |
| return rule.transform.call(null, context.expose(), options, node); | |
| } | |
| } | |
| return node; | |
| } | |
| // Extract an array of canonicalized rules from an object | |
| function rulesFromObject(object) { | |
| if (object[RULES]) { | |
| return object[RULES].map(({ match, transformKey }) => ({ | |
| match, | |
| transform: (...params) => object[transformKey](...params) | |
| })); | |
| } else if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["b" /* isArray */])(object)) { | |
| return object; | |
| } | |
| } | |
| /***/ }), | |
| /* 3 */ | |
| /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
| "use strict"; | |
| /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils__ = __webpack_require__(0); | |
| /* harmony export (immutable) */ __webpack_exports__["a"] = compileMatcher; | |
| /** | |
| * Given a specification for a matcher, compiles that spec into a function that will | |
| * take an AST node and a binding context and return a boolean indicating whether that | |
| * AST node matches the original spec. | |
| * | |
| * If the matcher returns true and the spec included binding directives, the context | |
| * will also be updated with bound values for those directives. | |
| */ | |
| function compileMatcher(spec) { | |
| if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__utils__["b" /* isArray */])(spec)) { | |
| return buildArrayMatcher(spec); | |
| } else if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__utils__["a" /* isObject */])(spec)) { | |
| return buildObjectMatcher(spec); | |
| } else if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__utils__["c" /* isFunction */])(spec)) { | |
| return spec; | |
| } else { | |
| return node => node === spec; | |
| } | |
| } | |
| function buildObjectMatcher(object) { | |
| let requiredKeys = Object.keys(object).sort(); | |
| let matchers = requiredKeys.map(key => compileMatcher(object[key])); | |
| return (node, context) => { | |
| if (!node || typeof node !== 'object') return false; | |
| let actualKeys = Object.keys(node).sort(); | |
| if (!arrayEqual(requiredKeys, actualKeys)) return false; | |
| return matchAll(context, matchers, requiredKeys.map(key => node[key])); | |
| }; | |
| } | |
| function buildArrayMatcher(spec) { | |
| let matchers = spec.map(value => compileMatcher(value)); | |
| return (node, context) => __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__utils__["b" /* isArray */])(node) && matchAll(context, matchers, node); | |
| } | |
| function arrayEqual(left, right) { | |
| if (left.length !== right.length) return false; | |
| for (let i = 0, len = left.length; i < len; i++) { | |
| if (left[i] !== right[i]) return false; | |
| } | |
| return true; | |
| } | |
| function matchAll(context, matchers, values) { | |
| if (matchers.length !== values.length) return false; | |
| let provisionalContext = context.createProvisionalContext(); | |
| for (let i = 0, len = matchers.length; i < len; i++) { | |
| if (!matchers[i](values[i], provisionalContext)) return false; | |
| } | |
| provisionalContext.commit(); | |
| return true; | |
| } | |
| /***/ }), | |
| /* 4 */ | |
| /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
| "use strict"; | |
| /** | |
| * Represents the context of bound variables for a given AST matcher. | |
| */ | |
| class Context { | |
| constructor(parent = null) { | |
| this.parent = parent; | |
| this.storage = Object.create(parent && parent.storage); | |
| } | |
| /** | |
| * Binds a value in this context, if possible. Returns a boolean indicating whether the | |
| * binding was successful (i.e., either the key was previously undefined or it already | |
| * contained the given value). | |
| */ | |
| bind(key, value) { | |
| if (key in this.storage) { | |
| if (this.storage[key] === value) { | |
| return true; | |
| } | |
| } else { | |
| this.storage[key] = value; | |
| return true; | |
| } | |
| } | |
| /** | |
| * Exposes the bound values on this context in a POJO. | |
| */ | |
| expose() { | |
| return Object.create(this.storage); | |
| } | |
| /** | |
| * Creates a provisional version of this context to bind values into. Those bound values | |
| * can later be committed to the context to make them permanent. | |
| */ | |
| createProvisionalContext() { | |
| return new Context(this); | |
| } | |
| /** | |
| * Applies all values bound on this provisional context to the parent that originally begat it. | |
| */ | |
| commit() { | |
| Object.keys(this.storage).forEach((key) => { | |
| this.parent.storage[key] = this.storage[key]; | |
| }); | |
| } | |
| } | |
| /* harmony export (immutable) */ __webpack_exports__["a"] = Context; | |
| /***/ }), | |
| /* 5 */ | |
| /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
| "use strict"; | |
| Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); | |
| /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__rules__ = __webpack_require__(2); | |
| /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__binding_matchers__ = __webpack_require__(1); | |
| /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "simple", function() { return __WEBPACK_IMPORTED_MODULE_1__binding_matchers__["a"]; }); | |
| /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "match", function() { return __WEBPACK_IMPORTED_MODULE_1__binding_matchers__["b"]; }); | |
| /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "sequence", function() { return __WEBPACK_IMPORTED_MODULE_1__binding_matchers__["c"]; }); | |
| /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "subtree", function() { return __WEBPACK_IMPORTED_MODULE_1__binding_matchers__["d"]; }); | |
| /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "choice", function() { return __WEBPACK_IMPORTED_MODULE_1__binding_matchers__["e"]; }); | |
| /* harmony export (immutable) */ __webpack_exports__["transform"] = transform; | |
| /* harmony export (immutable) */ __webpack_exports__["rule"] = rule; | |
| function transform(ruleSpecs) { | |
| let rules = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__rules__["a" /* extractRules */])(ruleSpecs); | |
| return (ast, options) => __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__rules__["b" /* applyRules */])(ast, rules, options); | |
| } | |
| function rule(spec, transformFunction) { | |
| if (transformFunction) { | |
| return __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__rules__["c" /* compileRule */])(spec, transformFunction); | |
| } else { | |
| return (target, key, descriptor) => { | |
| __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__rules__["d" /* memoizeRule */])(target, key, spec); | |
| return descriptor; | |
| }; | |
| } | |
| } | |
| /***/ }) | |
| /******/ ]); | |
| Object.assign(exports, botanist); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment