pax_global_header00006660000000000000000000000064131511014650014507gustar00rootroot0000000000000052 comment=2205c0f65d9595df1b1fddd7b09c79102ae34401 preact-8.2.5/000077500000000000000000000000001315110146500130015ustar00rootroot00000000000000preact-8.2.5/.babelrc000066400000000000000000000005541315110146500144000ustar00rootroot00000000000000{ "presets": [ [ "env", { "loose": true, "exclude": [ "transform-es2015-typeof-symbol" ], "targets": { "browsers": [ "last 2 versions", "IE >= 9" ] } } ] ], "plugins": [ "transform-object-rest-spread", "transform-react-jsx" ] } preact-8.2.5/.editorconfig000066400000000000000000000003541315110146500154600ustar00rootroot00000000000000root = true [*] indent_style = tab end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [{package.json,.*rc,*.yml}] indent_style = space indent_size = 2 [*.md] trim_trailing_whitespace = false preact-8.2.5/.gitignore000066400000000000000000000002111315110146500147630ustar00rootroot00000000000000/node_modules /npm-debug.log .DS_Store /dist /_dev /coverage # Additional bundles /devtools.js /devtools.js.map /debug.js /debug.js.map preact-8.2.5/.travis.yml000066400000000000000000000013111315110146500151060ustar00rootroot00000000000000sudo: false language: node_js node_js: - "6" cache: directories: - node_modules # Make chrome browser available for testing before_install: - export CHROME_BIN=chromium-browser - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start install: - npm install script: - npm run build - npm run test - BROWSER=true COVERAGE=false FLAKEY=false PERFORMANCE=false npm run test:karma - ./node_modules/coveralls/bin/coveralls.js < ./coverage/lcov.info # Necessary to compile native modules for io.js v3 or Node.js v4 env: - CXX=g++-4.8 # Necessary to compile native modules for io.js v3 or Node.js v4 addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-4.8 preact-8.2.5/CODE_OF_CONDUCT.md000066400000000000000000000062171315110146500156060ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@preactjs.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ preact-8.2.5/LICENSE000066400000000000000000000020671315110146500140130ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2017 Jason Miller Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. preact-8.2.5/README.md000066400000000000000000000722201315110146500142630ustar00rootroot00000000000000

Preact

Fast 3kB alternative to React, with the same ES2015 API.

**All the power of Virtual DOM components, without the overhead:** - Familiar React API & patterns: [ES6 Class] and [Functional Components] - Extensive React compatibility via a simple [preact-compat] alias - Everything you need: JSX, VDOM, React DevTools, HMR, SSR.. - A highly optimized diff algorithm and seamless Server Side Rendering - Transparent asynchronous rendering with a pluggable scheduler - 🆕💥 **Instant no-config app bundling with [Preact CLI](https://github.com/developit/preact-cli)** ### 💁 More information at the [Preact Website ➞](https://preactjs.com) --- - [Demos](#demos) - [Libraries & Add-ons](#libraries--add-ons) - [Getting Started](#getting-started) - [Import what you need](#import-what-you-need) - [Rendering JSX](#rendering-jsx) - [Components](#components) - [Props & State](#props--state) - [Linked State](#linked-state) - [Examples](#examples) - [Extensions](#extensions) - [Developer Tools](#developer-tools) - [Backers](#backers) - [Sponsors](#sponsors) - [License](#license) # Preact [![npm](https://img.shields.io/npm/v/preact.svg)](http://npm.im/preact) [![CDNJS](https://img.shields.io/cdnjs/v/preact.svg)](https://cdnjs.com/libraries/preact) [![Preact Slack Community](https://preact-slack.now.sh/badge.svg)](https://preact-slack.now.sh) [![OpenCollective Backers](https://opencollective.com/preact/backers/badge.svg)](#backers) [![OpenCollective Sponsors](https://opencollective.com/preact/sponsors/badge.svg)](#sponsors) [![travis](https://travis-ci.org/developit/preact.svg?branch=master)](https://travis-ci.org/developit/preact) [![coveralls](https://img.shields.io/coveralls/developit/preact/master.svg)](https://coveralls.io/github/developit/preact) [![gzip size](http://img.badgesize.io/https://unpkg.com/preact/dist/preact.min.js?compression=gzip)](https://unpkg.com/preact/dist/preact.min.js) Preact supports modern browsers and IE9+: [![Browsers](https://saucelabs.com/browser-matrix/preact.svg)](https://saucelabs.com/u/preact) --- ## Demos - [**ESBench**](http://esbench.com) is built using Preact. - [**Nectarine.rocks**](http://nectarine.rocks) _([GitHub Project](https://github.com/developit/nectarine))_ :peach: - [**Documentation Viewer**](https://documentation-viewer.firebaseapp.com) _([GitHub Project](https://github.com/developit/documentation-viewer))_ - [**TodoMVC**](https://preact-todomvc.surge.sh) _([GitHub Project](https://github.com/developit/preact-todomvc))_ - [**Hacker News Minimal**](https://developit.github.io/hn_minimal/) _([GitHub Project](https://github.com/developit/hn_minimal))_ - [**Preact Boilerplate**](https://preact-boilerplate.surge.sh) _([GitHub Project](https://github.com/developit/preact-boilerplate))_ :zap: - [**Preact Offline Starter**](https://preact-starter.now.sh) _([GitHub Project](https://github.com/lukeed/preact-starter))_ :100: - [**Preact PWA**](https://preact-pwa.appspot.com/) _([GitHub Project](https://github.com/ezekielchentnik/preact-pwa))_ :hamburger: - [**Preact Mobx Starter**](https://awaw00.github.io/preact-mobx-starter/) _([GitHub Project](https://github.com/awaw00/preact-mobx-starter))_ :sunny: - [**Preact Redux Example**](https://github.com/developit/preact-redux-example) :star: - [**Flickr Browser**](http://codepen.io/developit/full/VvMZwK/) (@ CodePen) - [**Animating Text**](http://codepen.io/developit/full/LpNOdm/) (@ CodePen) - [**60FPS Rainbow Spiral**](http://codepen.io/developit/full/xGoagz/) (@ CodePen) - [**Simple Clock**](http://jsfiddle.net/developit/u9m5x0L7/embedded/result,js/) (@ JSFiddle) - [**3D + ThreeJS**](http://codepen.io/developit/pen/PPMNjd?editors=0010) (@ CodePen) - [**Stock Ticker**](http://codepen.io/developit/pen/wMYoBb?editors=0010) (@ CodePen) - [**Create your Own!**](https://jsfiddle.net/developit/rs6zrh5f/embedded/result/) (@ JSFiddle) - [**Preact Coffeescript**](https://github.com/crisward/preact-coffee) - [**GuriVR**](https://gurivr.com) _([GitHub Project](https://github.com/opennewslabs/guri-vr))_ - [**V2EX Preact**](https://github.com/yanni4night/v2ex-preact) - [**BigWebQuiz**](https://bigwebquiz.com/) _([GitHub Project](https://github.com/jakearchibald/big-web-quiz))_ - [**Color Picker**](https://colors.now.sh) _([GitHub Project](https://github.com/lukeed/colors-app))_ :art: - [**Rainbow Explorer**](https://use-the-platform.com/rainbow-explorer/) _([GitHub Project](https://github.com/vaneenige/rainbow-explorer/))_ :rainbow: - [**Offline Gallery**](https://use-the-platform.com/offline-gallery/) _([GitHub Project](https://github.com/vaneenige/offline-gallery/))_ :balloon: - [**Periodic Weather**](https://use-the-platform.com/periodic-weather/) _([GitHub Project](https://github.com/vaneenige/periodic-weather/))_ :sunny: - [**Play.cash**](https://play.cash) :notes: - [**Rugby News Board**](http://nbrugby.com) _[(GitHub Project)](https://github.com/rugby-board/rugby-board-node)_ ## Libraries & Add-ons - :raised_hands: [**preact-compat**](https://git.io/preact-compat): use any React library with Preact *([full example](http://git.io/preact-compat-example))* - :page_facing_up: [**preact-render-to-string**](https://git.io/preact-render-to-string): Universal rendering. - :loop: [**preact-render-to-json**](https://git.io/preact-render-to-json): Render for Jest Snapshot testing. - :earth_americas: [**preact-router**](https://git.io/preact-router): URL routing for your components - :bookmark_tabs: [**preact-markup**](https://git.io/preact-markup): Render HTML & Custom Elements as JSX & Components - :satellite: [**preact-portal**](https://git.io/preact-portal): Render Preact components into (a) SPACE :milky_way: - :pencil: [**preact-richtextarea**](https://git.io/preact-richtextarea): Simple HTML editor component - :bookmark: [**preact-token-input**](https://github.com/developit/preact-token-input): Text field that tokenizes input, for things like tags - :card_index: [**preact-virtual-list**](https://github.com/developit/preact-virtual-list): Easily render lists with millions of rows ([demo](https://jsfiddle.net/developit/qqan9pdo/)) - :repeat: [**preact-cycle**](https://git.io/preact-cycle): Functional-reactive paradigm for Preact - :triangular_ruler: [**preact-layout**](https://download.github.io/preact-layout/): Small and simple layout library - :thought_balloon: [**preact-socrates**](https://github.com/matthewmueller/preact-socrates): Preact plugin for [Socrates](http://github.com/matthewmueller/socrates) - :rowboat: [**preact-flyd**](https://github.com/xialvjun/preact-flyd): Use [flyd](https://github.com/paldepind/flyd) FRP streams in Preact + JSX - :speech_balloon: [**preact-i18nline**](https://github.com/download/preact-i18nline): Integrates the ecosystem around [i18n-js](https://github.com/everydayhero/i18n-js) with Preact via [i18nline](https://github.com/download/i18nline). - :microscope: [**preact-jsx-chai**](https://git.io/preact-jsx-chai): JSX assertion testing _(no DOM, right in Node)_ - :tophat: [**preact-classless-component**](https://github.com/ld0rman/preact-classless-component): create preact components without the class keyword - :hammer: [**preact-hyperscript**](https://github.com/queckezz/preact-hyperscript): Hyperscript-like syntax for creating elements - :white_check_mark: [**shallow-compare**](https://github.com/tkh44/shallow-compare): simplified `shouldComponentUpdate` helper. - :shaved_ice: [**preact-codemod**](https://github.com/vutran/preact-codemod): Transform your React code to Preact. - :construction_worker: [**preact-helmet**](https://github.com/download/preact-helmet): A document head manager for Preact - :necktie: [**preact-delegate**](https://github.com/NekR/preact-delegate): Delegate DOM events #### UI Component Libraries > Want to prototype something or speed up your development? Try one of these toolkits: - [**preact-material-components**](https://github.com/prateekbh/preact-material-components): Material Design Components for Preact ([website](https://prateekbh.github.io/preact-material-components/)) - [**preact-mdc**](https://github.com/BerndWessels/preact-mdc): Material Design Components for Preact ([demo](https://github.com/BerndWessels/preact-mdc-demo)) - [**preact-mui**](https://git.io/v1aVO): The MUI CSS Preact library. - [**preact-photon**](https://git.io/preact-photon): build beautiful desktop UI with [photon](http://photonkit.com) - [**preact-mdl**](https://git.io/preact-mdl): [Material Design Lite](https://getmdl.io) for Preact - [**preact-weui**](https://github.com/afeiship/preact-weui): [Weui](https://github.com/afeiship/preact-weui) for Preact --- ## Getting Started > 💁 _**Note:** You [don't need ES2015 to use Preact](https://github.com/developit/preact-in-es3)... but give it a try!_ The easiest way to get started with Preact is to install [Preact CLI](https://github.com/developit/preact-cli). This simple command-line tool wraps up the best possible Webpack and Babel setup for you, and even keeps you up-to-date as the underlying tools change. Best of all, it's easy to understand! It builds your app in a single command (`preact build`), doesn't need any configuration, and bakes in best-practises 🙌. The following guide assumes you have some sort of ES2015 build set up using babel and/or webpack/browserify/gulp/grunt/etc. You can also start with [preact-boilerplate] or a [CodePen Template](http://codepen.io/developit/pen/pgaROe?editors=0010). ### Import what you need The `preact` module provides both named and default exports, so you can either import everything under a namespace of your choosing, or just what you need as locals: ##### Named: ```js import { h, render, Component } from 'preact'; // Tell Babel to transform JSX into h() calls: /** @jsx h */ ``` ##### Default: ```js import preact from 'preact'; // Tell Babel to transform JSX into preact.h() calls: /** @jsx preact.h */ ``` > Named imports work well for highly structured applications, whereas the default import is quick and never needs to be updated when using different parts of the library. > > Instead of declaring the `@jsx` pragma in your code, it's best to configure it globally in a `.babelrc`: > > **For Babel 5 and prior:** > > ```json > { "jsxPragma": "h" } > ``` > > **For Babel 6:** > > ```json > { > "plugins": [ > ["transform-react-jsx", { "pragma":"h" }] > ] > } > ``` ### Rendering JSX Out of the box, Preact provides an `h()` function that turns your JSX into Virtual DOM elements _([here's how](http://jasonformat.com/wtf-is-jsx))_. It also provides a `render()` function that creates a DOM tree from that Virtual DOM. To render some JSX, just import those two functions and use them like so: ```js import { h, render } from 'preact'; render((
Hello, world!
), document.body); ``` This should seem pretty straightforward if you've used hyperscript or one of its many friends. If you're not, the short of it is that the h function import gets used in the final, transpiled code as a drop in replacement for React.createElement, and so needs to be imported even if you don't explicitly use it in the code you write. Also note that if you're the kind of person who likes writing your React code in "pure JavaScript" (you know who you are) you will need to use h(...) wherever you would otherwise use React.createElement. Rendering hyperscript with a virtual DOM is pointless, though. We want to render components and have them updated when data changes - that's where the power of virtual DOM diffing shines. :star2: ### Components Preact exports a generic `Component` class, which can be extended to build encapsulated, self-updating pieces of a User Interface. Components support all of the standard React [lifecycle methods], like `shouldComponentUpdate()` and `componentWillReceiveProps()`. Providing specific implementations of these methods is the preferred mechanism for controlling _when_ and _how_ components update. Components also have a `render()` method, but unlike React this method is passed `(props, state)` as arguments. This provides an ergonomic means to destructure `props` and `state` into local variables to be referenced from JSX. Let's take a look at a very simple `Clock` component, which shows the current time. ```js import { h, render, Component } from 'preact'; class Clock extends Component { render() { let time = new Date().toLocaleTimeString(); return { time }; } } // render an instance of Clock into : render(, document.body); ``` That's great. Running this produces the following HTML DOM structure: ```html 10:28:57 PM ``` In order to have the clock's time update every second, we need to know when `` gets mounted to the DOM. _If you've used HTML5 Custom Elements, this is similar to the `attachedCallback` and `detachedCallback` lifecycle methods._ Preact invokes the following lifecycle methods if they are defined for a Component: | Lifecycle method | When it gets called | |-----------------------------|--------------------------------------------------| | `componentWillMount` | before the component gets mounted to the DOM | | `componentDidMount` | after the component gets mounted to the DOM | | `componentWillUnmount` | prior to removal from the DOM | | `componentWillReceiveProps` | before new props get accepted | | `shouldComponentUpdate` | before `render()`. Return `false` to skip render | | `componentWillUpdate` | before `render()` | | `componentDidUpdate` | after `render()` | So, we want to have a 1-second timer start once the Component gets added to the DOM, and stop if it is removed. We'll create the timer and store a reference to it in `componentDidMount`, and stop the timer in `componentWillUnmount`. On each timer tick, we'll update the component's `state` object with a new time value. Doing this will automatically re-render the component. ```js import { h, render, Component } from 'preact'; class Clock extends Component { constructor() { super(); // set initial time: this.state = { time: Date.now() }; } componentDidMount() { // update time every second this.timer = setInterval(() => { this.setState({ time: Date.now() }); }, 1000); } componentWillUnmount() { // stop when not renderable clearInterval(this.timer); } render(props, state) { let time = new Date(state.time).toLocaleTimeString(); return { time }; } } // render an instance of Clock into : render(, document.body); ``` Now we have [a ticking clock](http://jsfiddle.net/developit/u9m5x0L7/embedded/result,js/)! ### Props & State The concept (and nomenclature) for `props` and `state` is the same as in React. `props` are passed to a component by defining attributes in JSX, `state` is internal state. Changing either triggers a re-render, though by default Preact re-renders Components asynchronously for `state` changes and synchronously for `props` changes. You can tell Preact to render `prop` changes asynchronously by setting `options.syncComponentUpdates` to `false`. --- ## Linked State One area Preact takes a little further than React is in optimizing state changes. A common pattern in ES2015 React code is to use Arrow functions within a `render()` method in order to update state in response to events. Creating functions enclosed in a scope on every render is inefficient and forces the garbage collector to do more work than is necessary. One solution to this is to bind component methods declaratively. Here is an example using [decko](http://git.io/decko): ```js class Foo extends Component { @bind updateText(e) { this.setState({ text: e.target.value }); } render({ }, { text }) { return ; } } ``` While this achieves much better runtime performance, it's still a lot of unnecessary code to wire up state to UI. Fortunately there is a solution, in the form of a module called [linkstate](https://github.com/developit/linkstate). Calling `linkState(component, 'text')` returns a function that accepts an Event and uses it's associated value to update the given property in your component's state. Calls to `linkState()` with the same arguments are cached, so there is no performance penalty. Here is the previous example rewritten using _Linked State_: ```js import linkState from 'linkstate'; class Foo extends Component { render({ }, { text }) { return ; } } ``` Simple and effective. It handles linking state from any input type, or an optional second parameter can be used to explicitly provide a keypath to the new state value. > **Note:** In Preact 7 and prior, `linkState()` was built right into Component. In 8.0, it was moved to a separate module. You can restore the 7.x behavior by using linkstate as a polyfill - see [the linkstate docs](https://github.com/developit/linkstate#usage). ## Examples Here is a somewhat verbose Preact `` component: ```js class Link extends Component { render(props, state) { return {props.children}; } } ``` Since this is ES6/ES2015, we can further simplify: ```js class Link extends Component { render({ href, children }) { return ; } } // or, for wide-open props support: class Link extends Component { render(props) { return ; } } // or, as a stateless functional component: const Link = ({ children, ...props }) => ( { children } ); ``` ## Extensions It is likely that some projects based on Preact would wish to extend Component with great new functionality. Perhaps automatic connection to stores for a Flux-like architecture, or mixed-in context bindings to make it feel more like `React.createClass()`. Just use ES2015 inheritance: ```js class BoundComponent extends Component { constructor(props) { super(props); this.bind(); } bind() { this.binds = {}; for (let i in this) { this.binds[i] = this[i].bind(this); } } } // example usage class Link extends BoundComponent { click() { open(this.href); } render() { let { click } = this.binds; return { children }; } } ``` The possibilities are pretty endless here. You could even add support for rudimentary mixins: ```js class MixedComponent extends Component { constructor() { super(); (this.mixins || []).forEach( m => Object.assign(this, m) ); } } ``` ## Developer Tools You can inspect and modify the state of your Preact UI components at runtime using the [React Developer Tools](https://github.com/facebook/react-devtools) browser extension. 1. Install the [React Developer Tools](https://github.com/facebook/react-devtools) extension 2. Import the "preact/devtools" module in your app 3. Reload and go to the 'React' tab in the browser's development tools ```js import { h, Component, render } from 'preact'; // Enable devtools. You can reduce the size of your app by only including this // module in development builds. eg. In Webpack, wrap this with an `if (module.hot) {...}` // check. require('preact/devtools'); ``` ## Backers Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/preact#backer)] ## Sponsors Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor](https://opencollective.com/preact#sponsor)] ## License MIT [![Preact](http://i.imgur.com/YqCHvEW.gif)](https://preactjs.com) [preact-compat]: https://github.com/developit/preact-compat [ES6 Class]: https://facebook.github.io/react/docs/reusable-components.html#es6-classes [Functional Components]: https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html#stateless-functional-components [hyperscript]: https://github.com/dominictarr/hyperscript [preact-boilerplate]: https://github.com/developit/preact-boilerplate [lifecycle methods]: https://facebook.github.io/react/docs/component-specs.html preact-8.2.5/config/000077500000000000000000000000001315110146500142465ustar00rootroot00000000000000preact-8.2.5/config/codemod-const.js000066400000000000000000000022411315110146500173410ustar00rootroot00000000000000/* eslint no-console:0 */ /** Find constants (identified by ALL_CAPS_DECLARATIONS), and inline them globally. * This is safe because Preact *only* uses global constants. */ export default (file, api) => { let j = api.jscodeshift, code = j(file.source), constants = {}, found = 0; code.find(j.VariableDeclaration) .filter( decl => { for (let i=decl.value.declarations.length; i--; ) { let node = decl.value.declarations[i], name = node.id && node.id.name, init = node.init; if (name && init && name.match(/^[A-Z0-9_$]+$/g) && !init.regex) { if (init.type==='Literal') { // console.log(`Inlining constant: ${name}=${init.raw}`); found++; constants[name] = init; // remove declaration decl.value.declarations.splice(i, 1); // if it's the last, we'll remove the whole statement return !decl.value.declarations.length; } } } return false; }) .remove(); code.find(j.Identifier) .filter( path => path.value.name && constants.hasOwnProperty(path.value.name) ) .replaceWith( path => (found++, constants[path.value.name]) ); return found ? code.toSource({ quote: 'single' }) : null; }; preact-8.2.5/config/codemod-let-name.js000066400000000000000000000006701315110146500177210ustar00rootroot00000000000000/** * Restores var names transformed by babel's let block scoping */ export default (file, api) => { let j = api.jscodeshift; let code = j(file.source); // @TODO unsafe, but without it we gain 20b gzipped: https://www.diffchecker.com/bVrOJWTO code.findVariableDeclarators().filter(d => /^_i/.test(d.value.id.name)).renameTo('i'); code.findVariableDeclarators('_key').renameTo('key'); return code.toSource({ quote: 'single' }); }; preact-8.2.5/config/codemod-strip-tdz.js000066400000000000000000000025271315110146500201620ustar00rootroot00000000000000/* eslint no-console:0 */ // parent node types that we don't want to remove pointless initializations from (because it breaks hoisting) const BLOCKED = ['ForStatement', 'WhileStatement']; // 'IfStatement', 'SwitchStatement' /** Removes var initialization to `void 0`, which Babel adds for TDZ strictness. */ export default (file, api) => { let { jscodeshift } = api, found = 0; let code = jscodeshift(file.source) .find(jscodeshift.VariableDeclaration) .forEach(handleDeclaration); function handleDeclaration(decl) { let p = decl, remove = true; while ((p = p.parentPath)) { if (~BLOCKED.indexOf(p.value.type)) { remove = false; break; } } decl.value.declarations.filter(isPointless).forEach( node => { if (remove===false) { console.log(`> Skipping removal of undefined init for "${node.id.name}": within ${p.value.type}`); } else { removeNodeInitialization(node); } }); } function removeNodeInitialization(node) { node.init = null; found++; } function isPointless(node) { let { init } = node; if (init) { if (init.type==='UnaryExpression' && init.operator==='void' && init.argument.value==0) { return true; } if (init.type==='Identifier' && init.name==='undefined') { return true; } } return false; } return found ? code.toSource({ quote: 'single' }) : null; }; preact-8.2.5/config/eslint-config.js000066400000000000000000000025551315110146500173540ustar00rootroot00000000000000module.exports = { parser: 'babel-eslint', extends: 'eslint:recommended', plugins: [ 'react' ], env: { browser: true, mocha: true, node: true, es6: true }, parserOptions: { ecmaFeatures: { modules: true, jsx: true } }, globals: { sinon: true, expect: true }, rules: { 'react/jsx-uses-react': 2, 'react/jsx-uses-vars': 2, 'no-unused-vars': [1, { varsIgnorePattern: '^h$' }], 'no-cond-assign': 1, 'no-empty': 0, 'no-console': 1, semi: 2, camelcase: 0, 'comma-style': 2, 'comma-dangle': [2, 'never'], indent: [2, 'tab', {SwitchCase: 1}], 'no-mixed-spaces-and-tabs': [2, 'smart-tabs'], 'no-trailing-spaces': [2, { skipBlankLines: true }], 'max-nested-callbacks': [2, 3], 'no-eval': 2, 'no-implied-eval': 2, 'no-new-func': 2, 'guard-for-in': 0, eqeqeq: 0, 'no-else-return': 2, 'no-redeclare': 2, 'no-dupe-keys': 2, radix: 2, strict: [2, 'never'], 'no-shadow': 0, 'callback-return': [1, ['callback', 'cb', 'next', 'done']], 'no-delete-var': 2, 'no-undef-init': 2, 'no-shadow-restricted-names': 2, 'handle-callback-err': 0, 'no-lonely-if': 2, 'keyword-spacing': 2, 'constructor-super': 2, 'no-this-before-super': 2, 'no-dupe-class-members': 2, 'no-const-assign': 2, 'prefer-spread': 2, 'no-useless-concat': 2, 'no-var': 2, 'object-shorthand': 2, 'prefer-arrow-callback': 2 } }; preact-8.2.5/config/properties.json000066400000000000000000000011341315110146500173340ustar00rootroot00000000000000{ "props": { "cname": 16, "props": { "$_dirty": "__d", "$_disable": "__x", "$_listeners": "__l", "$_renderCallbacks": "__h", "$__key": "__k", "$__ref": "__r", "$normalizedNodeName": "__n", "$nextBase": "__b", "$prevContext": "__c", "$prevProps": "__p", "$prevState": "__s", "$_parentComponent": "__u", "$_componentConstructor": "_componentConstructor", "$__html": "__html", "$_component": "_component", "$__preactattr_": "__preactattr_" } }, "vars": { "cname": -1, "props": {} } }preact-8.2.5/config/rollup.config.devtools.js000066400000000000000000000010431315110146500212210ustar00rootroot00000000000000import babel from 'rollup-plugin-babel'; export default { input: 'devtools/index.js', output: { format: 'umd', file: 'devtools.js', name: 'preactDevTools', sourcemap: true }, external: ['preact'], globals: { preact: 'preact' }, plugins: [ babel({ sourceMap: true, exclude: 'node_modules/**', babelrc: false, presets: [ ['env', { modules: false, loose: true, exclude: ['transform-es2015-typeof-symbol'], targets: { browsers: ['last 2 versions', 'IE >= 9'] } }] ] }) ] }; preact-8.2.5/config/rollup.config.esm.js000066400000000000000000000003141315110146500201460ustar00rootroot00000000000000import config from './rollup.config'; // ES output config.output.format = 'es'; config.output.file = 'dist/preact.esm.js'; // remove memory() plugin config.plugins.splice(0, 1); export default config; preact-8.2.5/config/rollup.config.js000066400000000000000000000014671315110146500173750ustar00rootroot00000000000000import nodeResolve from 'rollup-plugin-node-resolve'; import babel from 'rollup-plugin-babel'; import memory from 'rollup-plugin-memory'; export default { strict: true, input: 'src/preact.js', output: { format: 'iife', file: 'dist/preact.dev.js', name: 'preact', sourcemap: true }, plugins: [ memory({ path: 'src/preact.js', contents: ` import preact from './preact'; if (typeof module!='undefined') module.exports = preact; else self.preact = preact; ` }), nodeResolve({ main: true }), babel({ sourceMap: true, exclude: 'node_modules/**', babelrc: false, presets: [ ['env', { modules: false, loose: true, exclude: ['transform-es2015-typeof-symbol'], targets: { browsers: ['last 2 versions', 'IE >= 9'] } }] ] }) ] }; preact-8.2.5/debug/000077500000000000000000000000001315110146500140675ustar00rootroot00000000000000preact-8.2.5/debug/index.js000066400000000000000000000047561315110146500155500ustar00rootroot00000000000000/* eslint-disable no-console */ if (process.env.NODE_ENV === 'development') { const { options } = require('preact'); const oldVnodeOption = options.vnode; options.vnode = function(vnode) { const { nodeName, attributes, children } = vnode; if (nodeName === void 0) { console.error('Undefined component passed to preact.h()\n'+serializeVNode(vnode)); } if ( attributes && attributes.ref !== void 0 && typeof attributes.ref !== 'function' && !('$$typeof' in vnode) // allow string refs when preact-compat is installed ) { throw new Error( `Component's "ref" property should be a function,` + ` but [${typeof attributes.ref}] passed\n` + serializeVNode(vnode) ); } { const keys = {}; inspectChildren(children, (deepChild) => { if (!deepChild || deepChild.key==null) return; // In Preact, all keys are stored as object values, i.e. being strings const key = deepChild.key + ''; if (keys.hasOwnProperty(key)) { console.error( 'Following component has two or more children with the ' + 'same "key" attribute. This may cause glitches and misbehavior ' + 'in rendering process. Component: \n\n' + serializeVNode(vnode) ); // Return early to not spam the console return true; } keys[key] = true; }); } if (oldVnodeOption) oldVnodeOption.call(this, vnode); }; const inspectChildren = (children, inspect) => { if (!Array.isArray(children)) { children = [children]; } return children.some((child, i) => { if (Array.isArray(child)) { return inspectChildren(child, inspect); } return inspect(child, i); }); }; const serializeVNode = ({ nodeName, attributes, children }) => { if (typeof nodeName==='function') { nodeName = nodeName.name || nodeName.displayName; } let props = ''; if (attributes) { for (let attr in attributes) { if (attributes.hasOwnProperty(attr) && attr!=='children') { let value = attributes[attr]; // If it is an object but doesn't have toString(), use Object.toString if (typeof value==='function') { value = `function ${value.displayName || value.name}() {}`; } if (Object(value) === value && !value.toString) { value = Object.prototype.toString.call(value); } else { value = value + ''; } props += ` ${attr}=${JSON.stringify(value)}`; } } } return `<${nodeName}${props}${children && children.length ? ('>..') : ' />'}`; }; require('preact/devtools'); } preact-8.2.5/devtools/000077500000000000000000000000001315110146500146405ustar00rootroot00000000000000preact-8.2.5/devtools/devtools.js000066400000000000000000000300461315110146500170400ustar00rootroot00000000000000/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ import { options } from 'preact'; // Internal helpers from preact import { ATTR_KEY } from '../src/constants'; /** * Return a ReactElement-compatible object for the current state of a preact * component. */ function createReactElement(component) { return { type: component.constructor, key: component.key, ref: null, // Unsupported props: component.props }; } /** * Create a ReactDOMComponent-compatible object for a given DOM node rendered * by preact. * * This implements the subset of the ReactDOMComponent interface that * React DevTools requires in order to display DOM nodes in the inspector with * the correct type and properties. * * @param {Node} node */ function createReactDOMComponent(node) { const childNodes = node.nodeType === Node.ELEMENT_NODE ? Array.from(node.childNodes) : []; const isText = node.nodeType === Node.TEXT_NODE; return { // --- ReactDOMComponent interface _currentElement: isText ? node.textContent : { type: node.nodeName.toLowerCase(), props: node[ATTR_KEY] }, _renderedChildren: childNodes.map(child => { if (child._component) { return updateReactComponent(child._component); } return updateReactComponent(child); }), _stringText: isText ? node.textContent : null, // --- Additional properties used by preact devtools // A flag indicating whether the devtools have been notified about the // existence of this component instance yet. // This is used to send the appropriate notifications when DOM components // are added or updated between composite component updates. _inDevTools: false, node }; } /** * Return the name of a component created by a `ReactElement`-like object. * * @param {ReactElement} element */ function typeName(element) { if (typeof element.type === 'function') { return element.type.displayName || element.type.name; } return element.type; } /** * Return a ReactCompositeComponent-compatible object for a given preact * component instance. * * This implements the subset of the ReactCompositeComponent interface that * the DevTools requires in order to walk the component tree and inspect the * component's properties. * * See https://github.com/facebook/react-devtools/blob/e31ec5825342eda570acfc9bcb43a44258fceb28/backend/getData.js */ function createReactCompositeComponent(component) { const _currentElement = createReactElement(component); const node = component.base; let instance = { // --- ReactDOMComponent properties getName() { return typeName(_currentElement); }, _currentElement: createReactElement(component), props: component.props, state: component.state, forceUpdate: component.forceUpdate && component.forceUpdate.bind(component), setState: component.setState && component.setState.bind(component), // --- Additional properties used by preact devtools node }; // React DevTools exposes the `_instance` field of the selected item in the // component tree as `$r` in the console. `_instance` must refer to a // React Component (or compatible) class instance with `props` and `state` // fields and `setState()`, `forceUpdate()` methods. instance._instance = component; // If the root node returned by this component instance's render function // was itself a composite component, there will be a `_component` property // containing the child component instance. if (component._component) { instance._renderedComponent = updateReactComponent(component._component); } else { // Otherwise, if the render() function returned an HTML/SVG element, // create a ReactDOMComponent-like object for the DOM node itself. instance._renderedComponent = updateReactComponent(node); } return instance; } /** * Map of Component|Node to ReactDOMComponent|ReactCompositeComponent-like * object. * * The same React*Component instance must be used when notifying devtools * about the initial mount of a component and subsequent updates. */ let instanceMap = typeof Map==='function' && new Map(); /** * Update (and create if necessary) the ReactDOMComponent|ReactCompositeComponent-like * instance for a given preact component instance or DOM Node. * * @param {Component|Node} componentOrNode */ function updateReactComponent(componentOrNode) { const newInstance = componentOrNode instanceof Node ? createReactDOMComponent(componentOrNode) : createReactCompositeComponent(componentOrNode); if (instanceMap.has(componentOrNode)) { let inst = instanceMap.get(componentOrNode); Object.assign(inst, newInstance); return inst; } instanceMap.set(componentOrNode, newInstance); return newInstance; } function nextRootKey(roots) { return '.' + Object.keys(roots).length; } /** * Find all root component instances rendered by preact in `node`'s children * and add them to the `roots` map. * * @param {DOMElement} node * @param {[key: string] => ReactDOMComponent|ReactCompositeComponent} */ function findRoots(node, roots) { Array.from(node.childNodes).forEach(child => { if (child._component) { roots[nextRootKey(roots)] = updateReactComponent(child._component); } else { findRoots(child, roots); } }); } /** * Create a bridge for exposing preact's component tree to React DevTools. * * It creates implementations of the interfaces that ReactDOM passes to * devtools to enable it to query the component tree and hook into component * updates. * * See https://github.com/facebook/react/blob/59ff7749eda0cd858d5ee568315bcba1be75a1ca/src/renderers/dom/ReactDOM.js * for how ReactDOM exports its internals for use by the devtools and * the `attachRenderer()` function in * https://github.com/facebook/react-devtools/blob/e31ec5825342eda570acfc9bcb43a44258fceb28/backend/attachRenderer.js * for how the devtools consumes the resulting objects. */ function createDevToolsBridge() { // The devtools has different paths for interacting with the renderers from // React Native, legacy React DOM and current React DOM. // // Here we emulate the interface for the current React DOM (v15+) lib. // ReactDOMComponentTree-like object const ComponentTree = { getNodeFromInstance(instance) { return instance.node; }, getClosestInstanceFromNode(node) { while (node && !node._component) { node = node.parentNode; } return node ? updateReactComponent(node._component) : null; } }; // Map of root ID (the ID is unimportant) to component instance. let roots = {}; findRoots(document.body, roots); // ReactMount-like object // // Used by devtools to discover the list of root component instances and get // notified when new root components are rendered. const Mount = { _instancesByReactRootID: roots, // Stub - React DevTools expects to find this method and replace it // with a wrapper in order to observe new root components being added _renderNewRootComponent(/* instance, ... */) { } }; // ReactReconciler-like object const Reconciler = { // Stubs - React DevTools expects to find these methods and replace them // with wrappers in order to observe components being mounted, updated and // unmounted mountComponent(/* instance, ... */) { }, performUpdateIfNecessary(/* instance, ... */) { }, receiveComponent(/* instance, ... */) { }, unmountComponent(/* instance, ... */) { } }; /** Notify devtools that a new component instance has been mounted into the DOM. */ const componentAdded = component => { const instance = updateReactComponent(component); if (isRootComponent(component)) { instance._rootID = nextRootKey(roots); roots[instance._rootID] = instance; Mount._renderNewRootComponent(instance); } visitNonCompositeChildren(instance, childInst => { childInst._inDevTools = true; Reconciler.mountComponent(childInst); }); Reconciler.mountComponent(instance); }; /** Notify devtools that a component has been updated with new props/state. */ const componentUpdated = component => { const prevRenderedChildren = []; visitNonCompositeChildren(instanceMap.get(component), childInst => { prevRenderedChildren.push(childInst); }); // Notify devtools about updates to this component and any non-composite // children const instance = updateReactComponent(component); Reconciler.receiveComponent(instance); visitNonCompositeChildren(instance, childInst => { if (!childInst._inDevTools) { // New DOM child component childInst._inDevTools = true; Reconciler.mountComponent(childInst); } else { // Updated DOM child component Reconciler.receiveComponent(childInst); } }); // For any non-composite children that were removed by the latest render, // remove the corresponding ReactDOMComponent-like instances and notify // the devtools prevRenderedChildren.forEach(childInst => { if (!document.body.contains(childInst.node)) { instanceMap.delete(childInst.node); Reconciler.unmountComponent(childInst); } }); }; /** Notify devtools that a component has been unmounted from the DOM. */ const componentRemoved = component => { const instance = updateReactComponent(component); visitNonCompositeChildren(childInst => { instanceMap.delete(childInst.node); Reconciler.unmountComponent(childInst); }); Reconciler.unmountComponent(instance); instanceMap.delete(component); if (instance._rootID) { delete roots[instance._rootID]; } }; return { componentAdded, componentUpdated, componentRemoved, // Interfaces passed to devtools via __REACT_DEVTOOLS_GLOBAL_HOOK__.inject() ComponentTree, Mount, Reconciler }; } /** * Return `true` if a preact component is a top level component rendered by * `render()` into a container Element. */ function isRootComponent(component) { // `_parentComponent` is actually `__u` after minification if (component._parentComponent || component.__u) { // Component with a composite parent return false; } if (component.base.parentElement && component.base.parentElement[ATTR_KEY]) { // Component with a parent DOM element rendered by Preact return false; } return true; } /** * Visit all child instances of a ReactCompositeComponent-like object that are * not composite components (ie. they represent DOM elements or text) * * @param {Component} component * @param {(Component) => void} visitor */ function visitNonCompositeChildren(component, visitor) { if (component._renderedComponent) { if (!component._renderedComponent._component) { visitor(component._renderedComponent); visitNonCompositeChildren(component._renderedComponent, visitor); } } else if (component._renderedChildren) { component._renderedChildren.forEach(child => { visitor(child); if (!child._component) visitNonCompositeChildren(child, visitor); }); } } /** * Create a bridge between the preact component tree and React's dev tools * and register it. * * After this function is called, the React Dev Tools should be able to detect * "React" on the page and show the component tree. * * This function hooks into preact VNode creation in order to expose functional * components correctly, so it should be called before the root component(s) * are rendered. * * Returns a cleanup function which unregisters the hooks. */ export function initDevTools() { if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') { // React DevTools are not installed return; } // Notify devtools when preact components are mounted, updated or unmounted const bridge = createDevToolsBridge(); const nextAfterMount = options.afterMount; options.afterMount = component => { bridge.componentAdded(component); if (nextAfterMount) nextAfterMount(component); }; const nextAfterUpdate = options.afterUpdate; options.afterUpdate = component => { bridge.componentUpdated(component); if (nextAfterUpdate) nextAfterUpdate(component); }; const nextBeforeUnmount = options.beforeUnmount; options.beforeUnmount = component => { bridge.componentRemoved(component); if (nextBeforeUnmount) nextBeforeUnmount(component); }; // Notify devtools about this instance of "React" __REACT_DEVTOOLS_GLOBAL_HOOK__.inject(bridge); return () => { options.afterMount = nextAfterMount; options.afterUpdate = nextAfterUpdate; options.beforeUnmount = nextBeforeUnmount; }; } preact-8.2.5/devtools/index.js000066400000000000000000000000751315110146500163070ustar00rootroot00000000000000import { initDevTools } from './devtools'; initDevTools(); preact-8.2.5/package.json000066400000000000000000000131341315110146500152710ustar00rootroot00000000000000{ "name": "preact", "version": "8.2.5", "description": "Fast 3kb React alternative with the same ES6 API. Components & Virtual DOM.", "main": "dist/preact.js", "jsnext:main": "dist/preact.esm.js", "module": "dist/preact.esm.js", "dev:main": "dist/preact.dev.js", "minified:main": "dist/preact.min.js", "scripts": { "clean": "rimraf dist/ devtools.js devtools.js.map debug.js debug.js.map", "copy-flow-definition": "copyfiles -f src/preact.js.flow dist", "copy-typescript-definition": "copyfiles -f src/preact.d.ts dist", "build": "npm-run-all --silent clean transpile copy-flow-definition copy-typescript-definition strip optimize minify size", "transpile:main": "rollup -c config/rollup.config.js", "transpile:devtools": "rollup -c config/rollup.config.devtools.js", "transpile:esm": "rollup -c config/rollup.config.esm.js", "transpile:debug": "babel debug/ -o debug.js -s", "transpile": "npm-run-all transpile:main transpile:esm transpile:devtools transpile:debug", "optimize": "uglifyjs dist/preact.dev.js -c conditionals=false,sequences=false,loops=false,join_vars=false,collapse_vars=false --pure-funcs=Object.defineProperty --mangle-props --mangle-regex=\"/^(_|normalizedNodeName|nextBase|prev[CPS]|_parentC)/\" --name-cache config/properties.json -b width=120,quote_style=3 -o dist/preact.js -p relative --in-source-map dist/preact.dev.js.map --source-map dist/preact.js.map", "minify": "uglifyjs dist/preact.js -c collapse_vars,evaluate,screw_ie8,unsafe,loops=false,keep_fargs=false,pure_getters,unused,dead_code -m -o dist/preact.min.js -p relative --in-source-map dist/preact.js.map --source-map dist/preact.min.js.map", "strip:main": "jscodeshift --run-in-band -s -t config/codemod-strip-tdz.js dist/preact.dev.js && jscodeshift --run-in-band -s -t config/codemod-const.js dist/preact.dev.js && jscodeshift --run-in-band -s -t config/codemod-let-name.js dist/preact.dev.js", "strip:esm": "jscodeshift --run-in-band -s -t config/codemod-strip-tdz.js dist/preact.esm.js && jscodeshift --run-in-band -s -t config/codemod-const.js dist/preact.esm.js && jscodeshift --run-in-band -s -t config/codemod-let-name.js dist/preact.esm.js", "strip": "npm-run-all strip:main strip:esm", "size": "node -e \"process.stdout.write('gzip size: ')\" && gzip-size --raw dist/preact.min.js", "test": "npm-run-all lint --parallel test:mocha test:karma test:ts test:size", "test:ts": "tsc -p test/ts/", "test:mocha": "mocha --recursive --require babel-register test/shared test/node", "test:karma": "karma start test/karma.conf.js --single-run", "test:mocha:watch": "npm run test:mocha -- --watch", "test:karma:watch": "npm run test:karma -- no-single-run", "test:size": "bundlesize", "lint": "eslint debug devtools src test", "prepublish": "npm run build", "smart-release": "npm run build && npm test && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish", "release": "cross-env npm run smart-release", "postinstall": "node -e \"console.log('\\u001b[35m\\u001b[1mLove Preact? You can now donate to our open collective:\\u001b[22m\\u001b[39m\\n > \\u001b[34mhttps://opencollective.com/preact/donate\\u001b[0m')\"" }, "eslintConfig": { "extends": "./config/eslint-config.js" }, "typings": "./dist/preact.d.ts", "repository": { "type": "git", "url": "https://github.com/developit/preact.git" }, "files": [ "devtools", "debug", "src", "dist", "devtools.js", "devtools.js.map", "debug.js", "debug.js.map", "typings.json" ], "keywords": [ "preact", "react", "virtual dom", "vdom", "components", "virtual", "dom" ], "author": "Jason Miller ", "license": "MIT", "bugs": { "url": "https://github.com/developit/preact/issues" }, "homepage": "https://github.com/developit/preact", "devDependencies": { "babel-cli": "^6.24.1", "babel-core": "^6.24.1", "babel-eslint": "^7.2.3", "babel-loader": "^7.0.0", "babel-plugin-transform-object-rest-spread": "^6.23.0", "babel-plugin-transform-react-jsx": "^6.24.1", "babel-preset-env": "^1.5.1", "bundlesize": "^0.13.2", "chai": "^3.4.1", "copyfiles": "^1.0.0", "core-js": "^2.4.1", "coveralls": "^2.11.15", "cross-env": "^3.1.3", "diff": "^3.0.0", "eslint": "^3.0.0", "eslint-plugin-react": "^6.0.0", "gzip-size-cli": "^2.0.0", "isparta-loader": "^2.0.0", "jscodeshift": "^0.3.25", "karma": "^1.1.0", "karma-babel-preprocessor": "^5.2.2", "karma-chai": "^0.1.0", "karma-chai-sinon": "^0.1.5", "karma-chrome-launcher": "^2.0.0", "karma-coverage": "^1.0.0", "karma-mocha": "^1.1.1", "karma-mocha-reporter": "^2.0.4", "karma-phantomjs-launcher": "^1.0.1", "karma-sauce-launcher": "^1.1.0", "karma-source-map-support": "^1.2.0", "karma-sourcemap-loader": "^0.3.6", "karma-webpack": "^2.0.3", "mocha": "^3.0.1", "npm-run-all": "^4.0.0", "phantomjs-prebuilt": "^2.1.7", "rimraf": "^2.5.3", "rollup": "^0.48.2", "rollup-plugin-babel": "^3.0.2", "rollup-plugin-memory": "^2.0.0", "rollup-plugin-node-resolve": "^3.0.0", "sinon": "^2.2.0", "sinon-chai": "^2.8.0", "typescript": "^2.2.2", "uglify-js": "^2.7.5", "webpack": "^2.4.1" }, "greenkeeper": { "ignore": [ "babel-cli", "babel-core", "babel-eslint", "babel-loader", "jscodeshift", "rollup-plugin-babel" ] }, "bundlesize": [ { "path": "./dist/preact.min.js", "threshold": "4Kb" } ] } preact-8.2.5/src/000077500000000000000000000000001315110146500135705ustar00rootroot00000000000000preact-8.2.5/src/clone-element.js000066400000000000000000000003761315110146500166630ustar00rootroot00000000000000import { extend } from './util'; import { h } from './h'; export function cloneElement(vnode, props) { return h( vnode.nodeName, extend(extend({}, vnode.attributes), props), arguments.length>2 ? [].slice.call(arguments, 2) : vnode.children ); } preact-8.2.5/src/component.js000066400000000000000000000044321315110146500161330ustar00rootroot00000000000000import { FORCE_RENDER } from './constants'; import { extend } from './util'; import { renderComponent } from './vdom/component'; import { enqueueRender } from './render-queue'; /** Base Component class. * Provides `setState()` and `forceUpdate()`, which trigger rendering. * @public * * @example * class MyFoo extends Component { * render(props, state) { * return
; * } * } */ export function Component(props, context) { this._dirty = true; /** @public * @type {object} */ this.context = context; /** @public * @type {object} */ this.props = props; /** @public * @type {object} */ this.state = this.state || {}; } extend(Component.prototype, { /** Returns a `boolean` indicating if the component should re-render when receiving the given `props` and `state`. * @param {object} nextProps * @param {object} nextState * @param {object} nextContext * @returns {Boolean} should the component re-render * @name shouldComponentUpdate * @function */ /** Update component state by copying properties from `state` to `this.state`. * @param {object} state A hash of state properties to update with new values * @param {function} callback A function to be called once component state is updated */ setState(state, callback) { let s = this.state; if (!this.prevState) this.prevState = extend({}, s); extend(s, typeof state==='function' ? state(s, this.props) : state); if (callback) (this._renderCallbacks = (this._renderCallbacks || [])).push(callback); enqueueRender(this); }, /** Immediately perform a synchronous re-render of the component. * @param {function} callback A function to be called after component is re-rendered. * @private */ forceUpdate(callback) { if (callback) (this._renderCallbacks = (this._renderCallbacks || [])).push(callback); renderComponent(this, FORCE_RENDER); }, /** Accepts `props` and `state`, and returns a new Virtual DOM tree to build. * Virtual DOM is generally constructed via [JSX](http://jasonformat.com/wtf-is-jsx). * @param {object} props Props (eg: JSX attributes) received from parent element/component * @param {object} state The component's current state * @param {object} context Context object (if a parent component has provided context) * @returns VNode */ render() {} }); preact-8.2.5/src/constants.js000066400000000000000000000005211315110146500161400ustar00rootroot00000000000000// render modes export const NO_RENDER = 0; export const SYNC_RENDER = 1; export const FORCE_RENDER = 2; export const ASYNC_RENDER = 3; export const ATTR_KEY = '__preactattr_'; // DOM properties that should NOT have "px" added when numeric export const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i; preact-8.2.5/src/dom/000077500000000000000000000000001315110146500143475ustar00rootroot00000000000000preact-8.2.5/src/dom/index.js000066400000000000000000000065601315110146500160230ustar00rootroot00000000000000import { IS_NON_DIMENSIONAL } from '../constants'; import options from '../options'; /** Create an element with the given nodeName. * @param {String} nodeName * @param {Boolean} [isSvg=false] If `true`, creates an element within the SVG namespace. * @returns {Element} node */ export function createNode(nodeName, isSvg) { let node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName); node.normalizedNodeName = nodeName; return node; } /** Remove a child node from its parent if attached. * @param {Element} node The node to remove */ export function removeNode(node) { let parentNode = node.parentNode; if (parentNode) parentNode.removeChild(node); } /** Set a named attribute on the given Node, with special behavior for some names and event handlers. * If `value` is `null`, the attribute/handler will be removed. * @param {Element} node An element to mutate * @param {string} name The name/key to set, such as an event or attribute name * @param {any} old The last value that was set for this name/node pair * @param {any} value An attribute value, such as a function to be used as an event handler * @param {Boolean} isSvg Are we currently diffing inside an svg? * @private */ export function setAccessor(node, name, old, value, isSvg) { if (name==='className') name = 'class'; if (name==='key') { // ignore } else if (name==='ref') { if (old) old(null); if (value) value(node); } else if (name==='class' && !isSvg) { node.className = value || ''; } else if (name==='style') { if (!value || typeof value==='string' || typeof old==='string') { node.style.cssText = value || ''; } if (value && typeof value==='object') { if (typeof old!=='string') { for (let i in old) if (!(i in value)) node.style[i] = ''; } for (let i in value) { node.style[i] = typeof value[i]==='number' && IS_NON_DIMENSIONAL.test(i)===false ? (value[i]+'px') : value[i]; } } } else if (name==='dangerouslySetInnerHTML') { if (value) node.innerHTML = value.__html || ''; } else if (name[0]=='o' && name[1]=='n') { let useCapture = name !== (name=name.replace(/Capture$/, '')); name = name.toLowerCase().substring(2); if (value) { if (!old) node.addEventListener(name, eventProxy, useCapture); } else { node.removeEventListener(name, eventProxy, useCapture); } (node._listeners || (node._listeners = {}))[name] = value; } else if (name!=='list' && name!=='type' && !isSvg && name in node) { setProperty(node, name, value==null ? '' : value); if (value==null || value===false) node.removeAttribute(name); } else { let ns = isSvg && (name !== (name = name.replace(/^xlink\:?/, ''))); if (value==null || value===false) { if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase()); else node.removeAttribute(name); } else if (typeof value!=='function') { if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase(), value); else node.setAttribute(name, value); } } } /** Attempt to set a DOM property to the given value. * IE & FF throw for certain property-value combinations. */ function setProperty(node, name, value) { try { node[name] = value; } catch (e) { } } /** Proxy an event to hooked event handlers * @private */ function eventProxy(e) { return this._listeners[e.type](options.event && options.event(e) || e); } preact-8.2.5/src/h.js000066400000000000000000000027431315110146500143630ustar00rootroot00000000000000import { VNode } from './vnode'; import options from './options'; const stack = []; const EMPTY_CHILDREN = []; /** JSX/hyperscript reviver * Benchmarks: https://esbench.com/bench/57ee8f8e330ab09900a1a1a0 * @see http://jasonformat.com/wtf-is-jsx * @public */ export function h(nodeName, attributes) { let children=EMPTY_CHILDREN, lastSimple, child, simple, i; for (i=arguments.length; i-- > 2; ) { stack.push(arguments[i]); } if (attributes && attributes.children!=null) { if (!stack.length) stack.push(attributes.children); delete attributes.children; } while (stack.length) { if ((child = stack.pop()) && child.pop!==undefined) { for (i=child.length; i--; ) stack.push(child[i]); } else { if (typeof child==='boolean') child = null; if ((simple = typeof nodeName!=='function')) { if (child==null) child = ''; else if (typeof child==='number') child = String(child); else if (typeof child!=='string') simple = false; } if (simple && lastSimple) { children[children.length-1] += child; } else if (children===EMPTY_CHILDREN) { children = [child]; } else { children.push(child); } lastSimple = simple; } } let p = new VNode(); p.nodeName = nodeName; p.children = children; p.attributes = attributes==null ? undefined : attributes; p.key = attributes==null ? undefined : attributes.key; // if a "vnode hook" is defined, pass every created VNode to it if (options.vnode!==undefined) options.vnode(p); return p; } preact-8.2.5/src/options.js000066400000000000000000000012521315110146500156210ustar00rootroot00000000000000/** Global options * @public * @namespace options {Object} */ export default { /** If `true`, `prop` changes trigger synchronous component updates. * @name syncComponentUpdates * @type Boolean * @default true */ //syncComponentUpdates: true, /** Processes all created VNodes. * @param {VNode} vnode A newly-created VNode to normalize/process */ //vnode(vnode) { } /** Hook invoked after a component is mounted. */ // afterMount(component) { } /** Hook invoked after the DOM is updated with a component's latest render. */ // afterUpdate(component) { } /** Hook invoked immediately before a component is unmounted. */ // beforeUnmount(component) { } }; preact-8.2.5/src/preact.d.ts000066400000000000000000000500741315110146500156460ustar00rootroot00000000000000declare namespace preact { interface ComponentProps | FunctionalComponent> { children?:JSX.Element[]; key?:string | number | any; ref?:(el: C) => void; } interface DangerouslySetInnerHTML { __html: string; } interface PreactHTMLAttributes { dangerouslySetInnerHTML?:DangerouslySetInnerHTML; key?:string; ref?:(el?: Element) => void; } interface VNode { nodeName:ComponentConstructor|string; attributes:{[name:string]:any}; children:VNode[]; key:string; } interface ComponentLifecycle { componentWillMount?():void; componentDidMount?():void; componentWillUnmount?():void; componentWillReceiveProps?(nextProps:PropsType,nextContext:any):void; shouldComponentUpdate?(nextProps:PropsType,nextState:StateType,nextContext:any):boolean; componentWillUpdate?(nextProps:PropsType,nextState:StateType,nextContext:any):void; componentDidUpdate?(previousProps:PropsType,previousState:StateType,previousContext:any):void; } interface FunctionalComponent { (props?:PropsType & ComponentProps, context?:any):JSX.Element; displayName?:string; defaultProps?:any; } interface ComponentConstructor { new (props?:PropsType, context?: any):Component; } // Type alias for a component considered generally, whether stateless or stateful. type AnyComponent = FunctionalComponent | typeof Component; abstract class Component { constructor(props?:PropsType, context?:any); static displayName?:string; static defaultProps?:any; state:StateType; props:PropsType & ComponentProps; context:any; base:HTMLElement; linkState:(name:string) => (event: Event) => void; setState(state:Pick, callback?:() => void):void; setState(fn:(prevState:StateType, props:PropsType) => Pick, callback?:() => void):void; forceUpdate(callback?:() => void): void; abstract render(props?:PropsType & ComponentProps, state?:StateType, context?:any):JSX.Element|null; } interface Component extends ComponentLifecycle { } function h(node:ComponentConstructor | FunctionalComponent, params:PropsType, ...children:(JSX.Element|JSX.Element[]|string)[]):JSX.Element; function h(node:string, params:JSX.HTMLAttributes&JSX.SVGAttributes&{[propName: string]: any}, ...children:(JSX.Element|JSX.Element[]|string)[]):JSX.Element; function render(node:JSX.Element, parent:Element|Document, mergeWith?:Element):Element; function rerender():void; function cloneElement(element:JSX.Element, props:any):JSX.Element; var options:{ syncComponentUpdates?:boolean; debounceRendering?:(render:() => void) => void; vnode?:(vnode:VNode) => void; event?:(event:Event) => Event; }; } declare module "preact" { export = preact; } declare module "preact/devtools" { // Empty. This module initializes the React Developer Tools integration // when imported. } declare namespace JSX { interface Element extends preact.VNode { } interface ElementClass extends preact.Component { } interface ElementAttributesProperty { props:any; } interface SVGAttributes extends HTMLAttributes { accentHeight?:number | string; accumulate?:"none" | "sum"; additive?:"replace" | "sum"; alignmentBaseline?:"auto" | "baseline" | "before-edge" | "text-before-edge" | "middle" | "central" | "after-edge" | "text-after-edge" | "ideographic" | "alphabetic" | "hanging" | "mathematical" | "inherit"; allowReorder?:"no" | "yes"; alphabetic?:number | string; amplitude?:number | string; arabicForm?:"initial" | "medial" | "terminal" | "isolated"; ascent?:number | string; attributeName?:string; attributeType?:string; autoReverse?:number | string; azimuth?:number | string; baseFrequency?:number | string; baselineShift?:number | string; baseProfile?:number | string; bbox?:number | string; begin?:number | string; bias?:number | string; by?:number | string; calcMode?:number | string; capHeight?:number | string; clip?:number | string; clipPath?:string; clipPathUnits?:number | string; clipRule?:number | string; colorInterpolation?:number | string; colorInterpolationFilters?:"auto" | "sRGB" | "linearRGB" | "inherit"; colorProfile?:number | string; colorRendering?:number | string; contentScriptType?:number | string; contentStyleType?:number | string; cursor?:number | string; cx?:number | string; cy?:number | string; d?:string; decelerate?:number | string; descent?:number | string; diffuseConstant?:number | string; direction?:number | string; display?:number | string; divisor?:number | string; dominantBaseline?:number | string; dur?:number | string; dx?:number | string; dy?:number | string; edgeMode?:number | string; elevation?:number | string; enableBackground?:number | string; end?:number | string; exponent?:number | string; externalResourcesRequired?:number | string; fill?:string; fillOpacity?:number | string; fillRule?:"nonzero" | "evenodd" | "inherit"; filter?:string; filterRes?:number | string; filterUnits?:number | string; floodColor?:number | string; floodOpacity?:number | string; focusable?:number | string; fontFamily?:string; fontSize?:number | string; fontSizeAdjust?:number | string; fontStretch?:number | string; fontStyle?:number | string; fontVariant?:number | string; fontWeight?:number | string; format?:number | string; from?:number | string; fx?:number | string; fy?:number | string; g1?:number | string; g2?:number | string; glyphName?:number | string; glyphOrientationHorizontal?:number | string; glyphOrientationVertical?:number | string; glyphRef?:number | string; gradientTransform?:string; gradientUnits?:string; hanging?:number | string; horizAdvX?:number | string; horizOriginX?:number | string; ideographic?:number | string; imageRendering?:number | string; in2?:number | string; in?:string; intercept?:number | string; k1?:number | string; k2?:number | string; k3?:number | string; k4?:number | string; k?:number | string; kernelMatrix?:number | string; kernelUnitLength?:number | string; kerning?:number | string; keyPoints?:number | string; keySplines?:number | string; keyTimes?:number | string; lengthAdjust?:number | string; letterSpacing?:number | string; lightingColor?:number | string; limitingConeAngle?:number | string; local?:number | string; markerEnd?:string; markerHeight?:number | string; markerMid?:string; markerStart?:string; markerUnits?:number | string; markerWidth?:number | string; mask?:string; maskContentUnits?:number | string; maskUnits?:number | string; mathematical?:number | string; mode?:number | string; numOctaves?:number | string; offset?:number | string; opacity?:number | string; operator?:number | string; order?:number | string; orient?:number | string; orientation?:number | string; origin?:number | string; overflow?:number | string; overlinePosition?:number | string; overlineThickness?:number | string; paintOrder?:number | string; panose1?:number | string; pathLength?:number | string; patternContentUnits?:string; patternTransform?:number | string; patternUnits?:string; pointerEvents?:number | string; points?:string; pointsAtX?:number | string; pointsAtY?:number | string; pointsAtZ?:number | string; preserveAlpha?:number | string; preserveAspectRatio?:string; primitiveUnits?:number | string; r?:number | string; radius?:number | string; refX?:number | string; refY?:number | string; renderingIntent?:number | string; repeatCount?:number | string; repeatDur?:number | string; requiredExtensions?:number | string; requiredFeatures?:number | string; restart?:number | string; result?:string; rotate?:number | string; rx?:number | string; ry?:number | string; scale?:number | string; seed?:number | string; shapeRendering?:number | string; slope?:number | string; spacing?:number | string; specularConstant?:number | string; specularExponent?:number | string; speed?:number | string; spreadMethod?:string; startOffset?:number | string; stdDeviation?:number | string; stemh?:number | string; stemv?:number | string; stitchTiles?:number | string; stopColor?:string; stopOpacity?:number | string; strikethroughPosition?:number | string; strikethroughThickness?:number | string; string?:number | string; stroke?:string; strokeDasharray?:string | number; strokeDashoffset?:string | number; strokeLinecap?:"butt" | "round" | "square" | "inherit"; strokeLinejoin?:"miter" | "round" | "bevel" | "inherit"; strokeMiterlimit?:string; strokeOpacity?:number | string; strokeWidth?:number | string; surfaceScale?:number | string; systemLanguage?:number | string; tableValues?:number | string; targetX?:number | string; targetY?:number | string; textAnchor?:string; textDecoration?:number | string; textLength?:number | string; textRendering?:number | string; to?:number | string; transform?:string; u1?:number | string; u2?:number | string; underlinePosition?:number | string; underlineThickness?:number | string; unicode?:number | string; unicodeBidi?:number | string; unicodeRange?:number | string; unitsPerEm?:number | string; vAlphabetic?:number | string; values?:string; vectorEffect?:number | string; version?:string; vertAdvY?:number | string; vertOriginX?:number | string; vertOriginY?:number | string; vHanging?:number | string; vIdeographic?:number | string; viewBox?:string; viewTarget?:number | string; visibility?:number | string; vMathematical?:number | string; widths?:number | string; wordSpacing?:number | string; writingMode?:number | string; x1?:number | string; x2?:number | string; x?:number | string; xChannelSelector?:string; xHeight?:number | string; xlinkActuate?:string; xlinkArcrole?:string; xlinkHref?:string; xlinkRole?:string; xlinkShow?:string; xlinkTitle?:string; xlinkType?:string; xmlBase?:string; xmlLang?:string; xmlns?:string; xmlnsXlink?:string; xmlSpace?:string; y1?:number | string; y2?:number | string; y?:number | string; yChannelSelector?:string; z?:number | string; zoomAndPan?:string; } interface PathAttributes { d:string; } interface EventHandler { (event:E):void; } type ClipboardEventHandler = EventHandler; type CompositionEventHandler = EventHandler; type DragEventHandler = EventHandler; type FocusEventHandler = EventHandler; type KeyboardEventHandler = EventHandler; type MouseEventHandler = EventHandler; type TouchEventHandler = EventHandler; type UIEventHandler = EventHandler; type WheelEventHandler = EventHandler; type AnimationEventHandler = EventHandler; type TransitionEventHandler = EventHandler; type GenericEventHandler = EventHandler; interface DOMAttributes { // Image Events onLoad?:GenericEventHandler; // Clipboard Events onCopy?:ClipboardEventHandler; onCut?:ClipboardEventHandler; onPaste?:ClipboardEventHandler; // Composition Events onCompositionEnd?:CompositionEventHandler; onCompositionStart?:CompositionEventHandler; onCompositionUpdate?:CompositionEventHandler; // Focus Events onFocus?:FocusEventHandler; onBlur?:FocusEventHandler; // Form Events onChange?:GenericEventHandler; onInput?:GenericEventHandler; onSearch?:GenericEventHandler; onSubmit?:GenericEventHandler; // Keyboard Events onKeyDown?:KeyboardEventHandler; onKeyPress?:KeyboardEventHandler; onKeyUp?:KeyboardEventHandler; // Media Events onAbort?:GenericEventHandler; onCanPlay?:GenericEventHandler; onCanPlayThrough?:GenericEventHandler; onDurationChange?:GenericEventHandler; onEmptied?:GenericEventHandler; onEncrypted?:GenericEventHandler; onEnded?:GenericEventHandler; onLoadedData?:GenericEventHandler; onLoadedMetadata?:GenericEventHandler; onLoadStart?:GenericEventHandler; onPause?:GenericEventHandler; onPlay?:GenericEventHandler; onPlaying?:GenericEventHandler; onProgress?:GenericEventHandler; onRateChange?:GenericEventHandler; onSeeked?:GenericEventHandler; onSeeking?:GenericEventHandler; onStalled?:GenericEventHandler; onSuspend?:GenericEventHandler; onTimeUpdate?:GenericEventHandler; onVolumeChange?:GenericEventHandler; onWaiting?:GenericEventHandler; // MouseEvents onClick?:MouseEventHandler; onContextMenu?:MouseEventHandler; onDblClick?: MouseEventHandler; onDrag?:DragEventHandler; onDragEnd?:DragEventHandler; onDragEnter?:DragEventHandler; onDragExit?:DragEventHandler; onDragLeave?:DragEventHandler; onDragOver?:DragEventHandler; onDragStart?:DragEventHandler; onDrop?:DragEventHandler; onMouseDown?:MouseEventHandler; onMouseEnter?:MouseEventHandler; onMouseLeave?:MouseEventHandler; onMouseMove?:MouseEventHandler; onMouseOut?:MouseEventHandler; onMouseOver?:MouseEventHandler; onMouseUp?:MouseEventHandler; // Selection Events onSelect?:GenericEventHandler; // Touch Events onTouchCancel?:TouchEventHandler; onTouchEnd?:TouchEventHandler; onTouchMove?:TouchEventHandler; onTouchStart?:TouchEventHandler; // UI Events onScroll?:UIEventHandler; // Wheel Events onWheel?:WheelEventHandler; // Animation Events onAnimationStart?:AnimationEventHandler; onAnimationEnd?:AnimationEventHandler; onAnimationIteration?:AnimationEventHandler; // Transition Events onTransitionEnd?:TransitionEventHandler; } interface HTMLAttributes extends preact.PreactHTMLAttributes, DOMAttributes { // Standard HTML Attributes accept?:string; acceptCharset?:string; accessKey?:string; action?:string; allowFullScreen?:boolean; allowTransparency?:boolean; alt?:string; async?:boolean; autocomplete?:string; autofocus?:boolean; autoPlay?:boolean; capture?:boolean; cellPadding?:number | string; cellSpacing?:number | string; charSet?:string; challenge?:string; checked?:boolean; class?:string | { [key:string]: boolean }; className?:string | { [key:string]: boolean }; cols?:number; colSpan?:number; content?:string; contentEditable?:boolean; contextMenu?:string; controls?:boolean; coords?:string; crossOrigin?:string; data?:string; dateTime?:string; default?:boolean; defer?:boolean; dir?:string; disabled?:boolean; download?:any; draggable?:boolean; encType?:string; form?:string; formAction?:string; formEncType?:string; formMethod?:string; formNoValidate?:boolean; formTarget?:string; frameBorder?:number | string; headers?:string; height?:number | string; hidden?:boolean; high?:number; href?:string; hrefLang?:string; for?:string; httpEquiv?:string; icon?:string; id?:string; inputMode?:string; integrity?:string; is?:string; keyParams?:string; keyType?:string; kind?:string; label?:string; lang?:string; list?:string; loop?:boolean; low?:number; manifest?:string; marginHeight?:number; marginWidth?:number; max?:number | string; maxLength?:number; media?:string; mediaGroup?:string; method?:string; min?:number | string; minLength?:number; multiple?:boolean; muted?:boolean; name?:string; noValidate?:boolean; open?:boolean; optimum?:number; pattern?:string; placeholder?:string; poster?:string; preload?:string; radioGroup?:string; readOnly?:boolean; rel?:string; required?:boolean; role?:string; rows?:number; rowSpan?:number; sandbox?:string; scope?:string; scoped?:boolean; scrolling?:string; seamless?:boolean; selected?:boolean; shape?:string; size?:number; sizes?:string; slot?:string; span?:number; spellCheck?:boolean; src?:string; srcset?:string; srcDoc?:string; srcLang?:string; srcSet?:string; start?:number; step?:number | string; style?:any; summary?:string; tabIndex?:number; target?:string; title?:string; type?:string; useMap?:string; value?:string | string[]; width?:number | string; wmode?:string; wrap?:string; // RDFa Attributes about?:string; datatype?:string; inlist?:any; prefix?:string; property?:string; resource?:string; typeof?:string; vocab?:string; } interface IntrinsicElements { // HTML a:HTMLAttributes; abbr:HTMLAttributes; address:HTMLAttributes; area:HTMLAttributes; article:HTMLAttributes; aside:HTMLAttributes; audio:HTMLAttributes; b:HTMLAttributes; base:HTMLAttributes; bdi:HTMLAttributes; bdo:HTMLAttributes; big:HTMLAttributes; blockquote:HTMLAttributes; body:HTMLAttributes; br:HTMLAttributes; button:HTMLAttributes; canvas:HTMLAttributes; caption:HTMLAttributes; cite:HTMLAttributes; code:HTMLAttributes; col:HTMLAttributes; colgroup:HTMLAttributes; data:HTMLAttributes; datalist:HTMLAttributes; dd:HTMLAttributes; del:HTMLAttributes; details:HTMLAttributes; dfn:HTMLAttributes; dialog:HTMLAttributes; div:HTMLAttributes; dl:HTMLAttributes; dt:HTMLAttributes; em:HTMLAttributes; embed:HTMLAttributes; fieldset:HTMLAttributes; figcaption:HTMLAttributes; figure:HTMLAttributes; footer:HTMLAttributes; form:HTMLAttributes; h1:HTMLAttributes; h2:HTMLAttributes; h3:HTMLAttributes; h4:HTMLAttributes; h5:HTMLAttributes; h6:HTMLAttributes; head:HTMLAttributes; header:HTMLAttributes; hr:HTMLAttributes; html:HTMLAttributes; i:HTMLAttributes; iframe:HTMLAttributes; img:HTMLAttributes; input:HTMLAttributes; ins:HTMLAttributes; kbd:HTMLAttributes; keygen:HTMLAttributes; label:HTMLAttributes; legend:HTMLAttributes; li:HTMLAttributes; link:HTMLAttributes; main:HTMLAttributes; map:HTMLAttributes; mark:HTMLAttributes; menu:HTMLAttributes; menuitem:HTMLAttributes; meta:HTMLAttributes; meter:HTMLAttributes; nav:HTMLAttributes; noscript:HTMLAttributes; object:HTMLAttributes; ol:HTMLAttributes; optgroup:HTMLAttributes; option:HTMLAttributes; output:HTMLAttributes; p:HTMLAttributes; param:HTMLAttributes; picture:HTMLAttributes; pre:HTMLAttributes; progress:HTMLAttributes; q:HTMLAttributes; rp:HTMLAttributes; rt:HTMLAttributes; ruby:HTMLAttributes; s:HTMLAttributes; samp:HTMLAttributes; script:HTMLAttributes; section:HTMLAttributes; select:HTMLAttributes; slot:HTMLAttributes; small:HTMLAttributes; source:HTMLAttributes; span:HTMLAttributes; strong:HTMLAttributes; style:HTMLAttributes; sub:HTMLAttributes; summary:HTMLAttributes; sup:HTMLAttributes; table:HTMLAttributes; tbody:HTMLAttributes; td:HTMLAttributes; textarea:HTMLAttributes; tfoot:HTMLAttributes; th:HTMLAttributes; thead:HTMLAttributes; time:HTMLAttributes; title:HTMLAttributes; tr:HTMLAttributes; track:HTMLAttributes; u:HTMLAttributes; ul:HTMLAttributes; "var":HTMLAttributes; video:HTMLAttributes; wbr:HTMLAttributes; //SVG svg:SVGAttributes; animate:SVGAttributes; circle:SVGAttributes; clipPath:SVGAttributes; defs:SVGAttributes; ellipse:SVGAttributes; feBlend:SVGAttributes; feColorMatrix:SVGAttributes; feComponentTransfer:SVGAttributes; feComposite:SVGAttributes; feConvolveMatrix:SVGAttributes; feDiffuseLighting:SVGAttributes; feDisplacementMap:SVGAttributes; feFlood:SVGAttributes; feGaussianBlur:SVGAttributes; feImage:SVGAttributes; feMerge:SVGAttributes; feMergeNode:SVGAttributes; feMorphology:SVGAttributes; feOffset:SVGAttributes; feSpecularLighting:SVGAttributes; feTile:SVGAttributes; feTurbulence:SVGAttributes; filter:SVGAttributes; foreignObject:SVGAttributes; g:SVGAttributes; image:SVGAttributes; line:SVGAttributes; linearGradient:SVGAttributes; marker:SVGAttributes; mask:SVGAttributes; path:SVGAttributes; pattern:SVGAttributes; polygon:SVGAttributes; polyline:SVGAttributes; radialGradient:SVGAttributes; rect:SVGAttributes; stop:SVGAttributes; symbol:SVGAttributes; text:SVGAttributes; tspan:SVGAttributes; use:SVGAttributes; } } preact-8.2.5/src/preact.js000066400000000000000000000006571315110146500154140ustar00rootroot00000000000000import { h, h as createElement } from './h'; import { cloneElement } from './clone-element'; import { Component } from './component'; import { render } from './render'; import { rerender } from './render-queue'; import options from './options'; export default { h, createElement, cloneElement, Component, render, rerender, options }; export { h, createElement, cloneElement, Component, render, rerender, options }; preact-8.2.5/src/preact.js.flow000066400000000000000000000004201315110146500163460ustar00rootroot00000000000000/* @flow */ import { createElement as h, cloneElement, Component, render } from 'react'; export { h, cloneElement, Component, render }; export default { h, cloneElement, Component, render }; declare export function rerender(): void; declare export var options: Object; preact-8.2.5/src/render-queue.js000066400000000000000000000010001315110146500165160ustar00rootroot00000000000000import options from './options'; import { defer } from './util'; import { renderComponent } from './vdom/component'; /** Managed queue of dirty components to be re-rendered */ let items = []; export function enqueueRender(component) { if (!component._dirty && (component._dirty = true) && items.push(component)==1) { (options.debounceRendering || defer)(rerender); } } export function rerender() { let p, list = items; items = []; while ( (p = list.pop()) ) { if (p._dirty) renderComponent(p); } } preact-8.2.5/src/render.js000066400000000000000000000012311315110146500154020ustar00rootroot00000000000000import { diff } from './vdom/diff'; /** Render JSX into a `parent` Element. * @param {VNode} vnode A (JSX) VNode to render * @param {Element} parent DOM element to render into * @param {Element} [merge] Attempt to re-use an existing DOM tree rooted at `merge` * @public * * @example * // render a div into : * render(
hello!
, document.body); * * @example * // render a "Thing" component into #foo: * const Thing = ({ name }) => { name }; * render(, document.querySelector('#foo')); */ export function render(vnode, parent, merge) { return diff(merge, vnode, {}, false, parent, false); } preact-8.2.5/src/util.js000066400000000000000000000005711315110146500151060ustar00rootroot00000000000000/** Copy own-properties from `props` onto `obj`. * @returns obj * @private */ export function extend(obj, props) { for (let i in props) obj[i] = props[i]; return obj; } /** Call a function asynchronously, as soon as possible. * @param {Function} callback */ export const defer = typeof Promise=='function' ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout; preact-8.2.5/src/vdom/000077500000000000000000000000001315110146500145355ustar00rootroot00000000000000preact-8.2.5/src/vdom/component-recycler.js000066400000000000000000000023271315110146500207070ustar00rootroot00000000000000import { Component } from '../component'; /** Retains a pool of Components for re-use, keyed on component name. * Note: since component names are not unique or even necessarily available, these are primarily a form of sharding. * @private */ const components = {}; /** Reclaim a component for later re-use by the recycler. */ export function collectComponent(component) { let name = component.constructor.name; (components[name] || (components[name] = [])).push(component); } /** Create a component. Normalizes differences between PFC's and classful Components. */ export function createComponent(Ctor, props, context) { let list = components[Ctor.name], inst; if (Ctor.prototype && Ctor.prototype.render) { inst = new Ctor(props, context); Component.call(inst, props, context); } else { inst = new Component(props, context); inst.constructor = Ctor; inst.render = doRender; } if (list) { for (let i=list.length; i--; ) { if (list[i].constructor===Ctor) { inst.nextBase = list[i].nextBase; list.splice(i, 1); break; } } } return inst; } /** The `.render()` method for a PFC backing instance. */ function doRender(props, state, context) { return this.constructor(props, context); } preact-8.2.5/src/vdom/component.js000066400000000000000000000177121315110146500171050ustar00rootroot00000000000000import { SYNC_RENDER, NO_RENDER, FORCE_RENDER, ASYNC_RENDER, ATTR_KEY } from '../constants'; import options from '../options'; import { extend } from '../util'; import { enqueueRender } from '../render-queue'; import { getNodeProps } from './index'; import { diff, mounts, diffLevel, flushMounts, recollectNodeTree, removeChildren } from './diff'; import { createComponent, collectComponent } from './component-recycler'; import { removeNode } from '../dom/index'; /** Set a component's `props` (generally derived from JSX attributes). * @param {Object} props * @param {Object} [opts] * @param {boolean} [opts.renderSync=false] If `true` and {@link options.syncComponentUpdates} is `true`, triggers synchronous rendering. * @param {boolean} [opts.render=true] If `false`, no render will be triggered. */ export function setComponentProps(component, props, opts, context, mountAll) { if (component._disable) return; component._disable = true; if ((component.__ref = props.ref)) delete props.ref; if ((component.__key = props.key)) delete props.key; if (!component.base || mountAll) { if (component.componentWillMount) component.componentWillMount(); } else if (component.componentWillReceiveProps) { component.componentWillReceiveProps(props, context); } if (context && context!==component.context) { if (!component.prevContext) component.prevContext = component.context; component.context = context; } if (!component.prevProps) component.prevProps = component.props; component.props = props; component._disable = false; if (opts!==NO_RENDER) { if (opts===SYNC_RENDER || options.syncComponentUpdates!==false || !component.base) { renderComponent(component, SYNC_RENDER, mountAll); } else { enqueueRender(component); } } if (component.__ref) component.__ref(component); } /** Render a Component, triggering necessary lifecycle events and taking High-Order Components into account. * @param {Component} component * @param {Object} [opts] * @param {boolean} [opts.build=false] If `true`, component will build and store a DOM node if not already associated with one. * @private */ export function renderComponent(component, opts, mountAll, isChild) { if (component._disable) return; let props = component.props, state = component.state, context = component.context, previousProps = component.prevProps || props, previousState = component.prevState || state, previousContext = component.prevContext || context, isUpdate = component.base, nextBase = component.nextBase, initialBase = isUpdate || nextBase, initialChildComponent = component._component, skip = false, rendered, inst, cbase; // if updating if (isUpdate) { component.props = previousProps; component.state = previousState; component.context = previousContext; if (opts!==FORCE_RENDER && component.shouldComponentUpdate && component.shouldComponentUpdate(props, state, context) === false) { skip = true; } else if (component.componentWillUpdate) { component.componentWillUpdate(props, state, context); } component.props = props; component.state = state; component.context = context; } component.prevProps = component.prevState = component.prevContext = component.nextBase = null; component._dirty = false; if (!skip) { rendered = component.render(props, state, context); // context to pass to the child, can be updated via (grand-)parent component if (component.getChildContext) { context = extend(extend({}, context), component.getChildContext()); } let childComponent = rendered && rendered.nodeName, toUnmount, base; if (typeof childComponent==='function') { // set up high order component link let childProps = getNodeProps(rendered); inst = initialChildComponent; if (inst && inst.constructor===childComponent && childProps.key==inst.__key) { setComponentProps(inst, childProps, SYNC_RENDER, context, false); } else { toUnmount = inst; component._component = inst = createComponent(childComponent, childProps, context); inst.nextBase = inst.nextBase || nextBase; inst._parentComponent = component; setComponentProps(inst, childProps, NO_RENDER, context, false); renderComponent(inst, SYNC_RENDER, mountAll, true); } base = inst.base; } else { cbase = initialBase; // destroy high order component link toUnmount = initialChildComponent; if (toUnmount) { cbase = component._component = null; } if (initialBase || opts===SYNC_RENDER) { if (cbase) cbase._component = null; base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, true); } } if (initialBase && base!==initialBase && inst!==initialChildComponent) { let baseParent = initialBase.parentNode; if (baseParent && base!==baseParent) { baseParent.replaceChild(base, initialBase); if (!toUnmount) { initialBase._component = null; recollectNodeTree(initialBase, false); } } } if (toUnmount) { unmountComponent(toUnmount); } component.base = base; if (base && !isChild) { let componentRef = component, t = component; while ((t=t._parentComponent)) { (componentRef = t).base = base; } base._component = componentRef; base._componentConstructor = componentRef.constructor; } } if (!isUpdate || mountAll) { mounts.unshift(component); } else if (!skip) { // Ensure that pending componentDidMount() hooks of child components // are called before the componentDidUpdate() hook in the parent. // Note: disabled as it causes duplicate hooks, see https://github.com/developit/preact/issues/750 // flushMounts(); if (component.componentDidUpdate) { component.componentDidUpdate(previousProps, previousState, previousContext); } if (options.afterUpdate) options.afterUpdate(component); } if (component._renderCallbacks!=null) { while (component._renderCallbacks.length) component._renderCallbacks.pop().call(component); } if (!diffLevel && !isChild) flushMounts(); } /** Apply the Component referenced by a VNode to the DOM. * @param {Element} dom The DOM node to mutate * @param {VNode} vnode A Component-referencing VNode * @returns {Element} dom The created/mutated element * @private */ export function buildComponentFromVNode(dom, vnode, context, mountAll) { let c = dom && dom._component, originalComponent = c, oldDom = dom, isDirectOwner = c && dom._componentConstructor===vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode); while (c && !isOwner && (c=c._parentComponent)) { isOwner = c.constructor===vnode.nodeName; } if (c && isOwner && (!mountAll || c._component)) { setComponentProps(c, props, ASYNC_RENDER, context, mountAll); dom = c.base; } else { if (originalComponent && !isDirectOwner) { unmountComponent(originalComponent); dom = oldDom = null; } c = createComponent(vnode.nodeName, props, context); if (dom && !c.nextBase) { c.nextBase = dom; // passing dom/oldDom as nextBase will recycle it if unused, so bypass recycling on L229: oldDom = null; } setComponentProps(c, props, SYNC_RENDER, context, mountAll); dom = c.base; if (oldDom && dom!==oldDom) { oldDom._component = null; recollectNodeTree(oldDom, false); } } return dom; } /** Remove a component from the DOM and recycle it. * @param {Component} component The Component instance to unmount * @private */ export function unmountComponent(component) { if (options.beforeUnmount) options.beforeUnmount(component); let base = component.base; component._disable = true; if (component.componentWillUnmount) component.componentWillUnmount(); component.base = null; // recursively tear down & recollect high-order component children: let inner = component._component; if (inner) { unmountComponent(inner); } else if (base) { if (base[ATTR_KEY] && base[ATTR_KEY].ref) base[ATTR_KEY].ref(null); component.nextBase = base; removeNode(base); collectComponent(component); removeChildren(base); } if (component.__ref) component.__ref(null); } preact-8.2.5/src/vdom/diff.js000066400000000000000000000232521315110146500160070ustar00rootroot00000000000000import { ATTR_KEY } from '../constants'; import { isSameNodeType, isNamedNode } from './index'; import { buildComponentFromVNode } from './component'; import { createNode, setAccessor } from '../dom/index'; import { unmountComponent } from './component'; import options from '../options'; import { removeNode } from '../dom/index'; /** Queue of components that have been mounted and are awaiting componentDidMount */ export const mounts = []; /** Diff recursion count, used to track the end of the diff cycle. */ export let diffLevel = 0; /** Global flag indicating if the diff is currently within an SVG */ let isSvgMode = false; /** Global flag indicating if the diff is performing hydration */ let hydrating = false; /** Invoke queued componentDidMount lifecycle methods */ export function flushMounts() { let c; while ((c=mounts.pop())) { if (options.afterMount) options.afterMount(c); if (c.componentDidMount) c.componentDidMount(); } } /** Apply differences in a given vnode (and it's deep children) to a real DOM Node. * @param {Element} [dom=null] A DOM node to mutate into the shape of the `vnode` * @param {VNode} vnode A VNode (with descendants forming a tree) representing the desired DOM structure * @returns {Element} dom The created/mutated element * @private */ export function diff(dom, vnode, context, mountAll, parent, componentRoot) { // diffLevel having been 0 here indicates initial entry into the diff (not a subdiff) if (!diffLevel++) { // when first starting the diff, check if we're diffing an SVG or within an SVG isSvgMode = parent!=null && parent.ownerSVGElement!==undefined; // hydration is indicated by the existing element to be diffed not having a prop cache hydrating = dom!=null && !(ATTR_KEY in dom); } let ret = idiff(dom, vnode, context, mountAll, componentRoot); // append the element if its a new parent if (parent && ret.parentNode!==parent) parent.appendChild(ret); // diffLevel being reduced to 0 means we're exiting the diff if (!--diffLevel) { hydrating = false; // invoke queued componentDidMount lifecycle methods if (!componentRoot) flushMounts(); } return ret; } /** Internals of `diff()`, separated to allow bypassing diffLevel / mount flushing. */ function idiff(dom, vnode, context, mountAll, componentRoot) { let out = dom, prevSvgMode = isSvgMode; // empty values (null, undefined, booleans) render as empty Text nodes if (vnode==null || typeof vnode==='boolean') vnode = ''; // Fast case: Strings & Numbers create/update Text nodes. if (typeof vnode==='string' || typeof vnode==='number') { // update if it's already a Text node: if (dom && dom.splitText!==undefined && dom.parentNode && (!dom._component || componentRoot)) { /* istanbul ignore if */ /* Browser quirk that can't be covered: https://github.com/developit/preact/commit/fd4f21f5c45dfd75151bd27b4c217d8003aa5eb9 */ if (dom.nodeValue!=vnode) { dom.nodeValue = vnode; } } else { // it wasn't a Text node: replace it with one and recycle the old Element out = document.createTextNode(vnode); if (dom) { if (dom.parentNode) dom.parentNode.replaceChild(out, dom); recollectNodeTree(dom, true); } } out[ATTR_KEY] = true; return out; } // If the VNode represents a Component, perform a component diff: let vnodeName = vnode.nodeName; if (typeof vnodeName==='function') { return buildComponentFromVNode(dom, vnode, context, mountAll); } // Tracks entering and exiting SVG namespace when descending through the tree. isSvgMode = vnodeName==='svg' ? true : vnodeName==='foreignObject' ? false : isSvgMode; // If there's no existing element or it's the wrong type, create a new one: vnodeName = String(vnodeName); if (!dom || !isNamedNode(dom, vnodeName)) { out = createNode(vnodeName, isSvgMode); if (dom) { // move children into the replacement node while (dom.firstChild) out.appendChild(dom.firstChild); // if the previous Element was mounted into the DOM, replace it inline if (dom.parentNode) dom.parentNode.replaceChild(out, dom); // recycle the old element (skips non-Element node types) recollectNodeTree(dom, true); } } let fc = out.firstChild, props = out[ATTR_KEY], vchildren = vnode.children; if (props==null) { props = out[ATTR_KEY] = {}; for (let a=out.attributes, i=a.length; i--; ) props[a[i].name] = a[i].value; } // Optimization: fast-path for elements containing a single TextNode: if (!hydrating && vchildren && vchildren.length===1 && typeof vchildren[0]==='string' && fc!=null && fc.splitText!==undefined && fc.nextSibling==null) { if (fc.nodeValue!=vchildren[0]) { fc.nodeValue = vchildren[0]; } } // otherwise, if there are existing or new children, diff them: else if (vchildren && vchildren.length || fc!=null) { innerDiffNode(out, vchildren, context, mountAll, hydrating || props.dangerouslySetInnerHTML!=null); } // Apply attributes/props from VNode to the DOM Element: diffAttributes(out, vnode.attributes, props); // restore previous SVG mode: (in case we're exiting an SVG namespace) isSvgMode = prevSvgMode; return out; } /** Apply child and attribute changes between a VNode and a DOM Node to the DOM. * @param {Element} dom Element whose children should be compared & mutated * @param {Array} vchildren Array of VNodes to compare to `dom.childNodes` * @param {Object} context Implicitly descendant context object (from most recent `getChildContext()`) * @param {Boolean} mountAll * @param {Boolean} isHydrating If `true`, consumes externally created elements similar to hydration */ function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) { let originalChildren = dom.childNodes, children = [], keyed = {}, keyedLen = 0, min = 0, len = originalChildren.length, childrenLen = 0, vlen = vchildren ? vchildren.length : 0, j, c, f, vchild, child; // Build up a map of keyed children and an Array of unkeyed children: if (len!==0) { for (let i=0; i Object.keys(obj).forEach( key => sinon.spy(obj,key) ); function getAttributes(node) { let attrs = {}; if (node.attributes) { for (let i=node.attributes.length; i--; ) { attrs[node.attributes[i].name] = node.attributes[i].value; } } return attrs; } // hacky normalization of attribute order across browsers. function sortAttributes(html) { return html.replace(/<([a-z0-9-]+)((?:\s[a-z0-9:_.-]+=".*?")+)((?:\s*\/)?>)/gi, (s, pre, attrs, after) => { let list = attrs.match(/\s[a-z0-9:_.-]+=".*?"/gi).sort( (a, b) => a>b ? 1 : -1 ); if (~after.indexOf('/')) after = '>'; return '<' + pre + list.join('') + after; }); } const Empty = () => null; describe('Components', () => { let scratch; before( () => { scratch = document.createElement('div'); (document.body || document.documentElement).appendChild(scratch); }); beforeEach( () => { let c = scratch.firstElementChild; if (c) render(, scratch, c); scratch.innerHTML = ''; }); after( () => { scratch.parentNode.removeChild(scratch); scratch = null; }); it('should render components', () => { class C1 extends Component { render() { return
C1
; } } sinon.spy(C1.prototype, 'render'); render(, scratch); expect(C1.prototype.render) .to.have.been.calledOnce .and.to.have.been.calledWithMatch({}, {}) .and.to.have.returned(sinon.match({ nodeName:'div' })); expect(scratch.innerHTML).to.equal('
C1
'); }); it('should render functional components', () => { const PROPS = { foo:'bar', onBaz:()=>{} }; const C3 = sinon.spy( props =>
); render(, scratch); expect(C3) .to.have.been.calledOnce .and.to.have.been.calledWithMatch(PROPS) .and.to.have.returned(sinon.match({ nodeName: 'div', attributes: PROPS })); expect(scratch.innerHTML).to.equal('
'); }); it('should render components with props', () => { const PROPS = { foo:'bar', onBaz:()=>{} }; let constructorProps; class C2 extends Component { constructor(props) { super(props); constructorProps = props; } render(props) { return
; } } sinon.spy(C2.prototype, 'render'); render(, scratch); expect(constructorProps).to.deep.equal(PROPS); expect(C2.prototype.render) .to.have.been.calledOnce .and.to.have.been.calledWithMatch(PROPS, {}) .and.to.have.returned(sinon.match({ nodeName: 'div', attributes: PROPS })); expect(scratch.innerHTML).to.equal('
'); }); it('should clone components', () => { function Comp () {} let instance = ; let clone = cloneElement(instance); expect(clone.prototype).to.equal(instance.prototype); }); // Test for Issue #73 it('should remove orphaned elements replaced by Components', () => { class Comp extends Component { render() { return span in a component; } } let root; function test(content) { root = render(content, scratch, root); } test(); test(
just a div
); test(); expect(scratch.innerHTML).to.equal('span in a component'); }); // Test for Issue #176 it('should remove children when root changes to text node', () => { let comp; class Comp extends Component { render(_, { alt }) { return alt ? 'asdf' :
test
; } } render(comp=c} />, scratch); comp.setState({ alt:true }); comp.forceUpdate(); expect(scratch.innerHTML, 'switching to textnode').to.equal('asdf'); comp.setState({ alt:false }); comp.forceUpdate(); expect(scratch.innerHTML, 'switching to element').to.equal('
test
'); comp.setState({ alt:true }); comp.forceUpdate(); expect(scratch.innerHTML, 'switching to textnode 2').to.equal('asdf'); }); // Test for Issue #254 it('should not recycle common class children with different keys', () => { let idx = 0; let msgs = ['A','B','C','D','E','F','G','H']; let sideEffect = sinon.spy(); class Comp extends Component { componentWillMount() { this.innerMsg = msgs[(idx++ % 8)]; sideEffect(); } render() { return
{this.innerMsg}
; } } sinon.spy(Comp.prototype, 'componentWillMount'); class GoodContainer extends Component { constructor(props) { super(props); this.state.alt = false; } render(_, {alt}) { return (
{alt ? null : ()} {alt ? null : ()} {alt ? () : null}
); } } class BadContainer extends Component { constructor(props) { super(props); this.state.alt = false; } render(_, {alt}) { return (
{alt ? null : ()} {alt ? null : ()} {alt ? () : null}
); } } let good, bad; let root = render(good=c} />, scratch); expect(scratch.textContent, 'new component with key present').to.equal('AB'); expect(Comp.prototype.componentWillMount).to.have.been.calledTwice; expect(sideEffect).to.have.been.calledTwice; sideEffect.reset(); Comp.prototype.componentWillMount.reset(); good.setState({alt: true}); good.forceUpdate(); expect(scratch.textContent, 'new component with key present re-rendered').to.equal('C'); //we are recycling the first 2 components already rendered, just need a new one expect(Comp.prototype.componentWillMount).to.have.been.calledOnce; expect(sideEffect).to.have.been.calledOnce; sideEffect.reset(); Comp.prototype.componentWillMount.reset(); render(bad=c} />, scratch, root); expect(scratch.textContent, 'new component without key').to.equal('DE'); expect(Comp.prototype.componentWillMount).to.have.been.calledTwice; expect(sideEffect).to.have.been.calledTwice; sideEffect.reset(); Comp.prototype.componentWillMount.reset(); bad.setState({alt: true}); bad.forceUpdate(); expect(scratch.textContent, 'new component without key re-rendered').to.equal('D'); expect(Comp.prototype.componentWillMount).to.not.have.been.called; expect(sideEffect).to.not.have.been.called; }); describe('props.children', () => { it('should support passing children as a prop', () => { const Foo = props =>
; render(bar, '123', 456 ]} />, scratch); expect(scratch.innerHTML).to.equal('
bar123456
'); }); it('should be ignored when explicit children exist', () => { const Foo = props =>
a
; render(, scratch); expect(scratch.innerHTML).to.equal('
a
'); }); }); describe('High-Order Components', () => { it('should render nested functional components', () => { const PROPS = { foo:'bar', onBaz:()=>{} }; const Outer = sinon.spy( props => ); const Inner = sinon.spy( props =>
inner
); render(, scratch); expect(Outer) .to.have.been.calledOnce .and.to.have.been.calledWithMatch(PROPS) .and.to.have.returned(sinon.match({ nodeName: Inner, attributes: PROPS })); expect(Inner) .to.have.been.calledOnce .and.to.have.been.calledWithMatch(PROPS) .and.to.have.returned(sinon.match({ nodeName: 'div', attributes: PROPS, children: ['inner'] })); expect(scratch.innerHTML).to.equal('
inner
'); }); it('should re-render nested functional components', () => { let doRender = null; class Outer extends Component { componentDidMount() { let i = 1; doRender = () => this.setState({ i: ++i }); } componentWillUnmount() {} render(props, { i }) { return ; } } sinon.spy(Outer.prototype, 'render'); sinon.spy(Outer.prototype, 'componentWillUnmount'); let j = 0; const Inner = sinon.spy( props =>
inner
); render(, scratch); // update & flush doRender(); rerender(); expect(Outer.prototype.componentWillUnmount) .not.to.have.been.called; expect(Inner).to.have.been.calledTwice; expect(Inner.secondCall) .to.have.been.calledWithMatch({ foo:'bar', i:2 }) .and.to.have.returned(sinon.match({ attributes: { j: 2, i: 2, foo: 'bar' } })); expect(getAttributes(scratch.firstElementChild)).to.eql({ j: '2', i: '2', foo: 'bar' }); // update & flush doRender(); rerender(); expect(Inner).to.have.been.calledThrice; expect(Inner.thirdCall) .to.have.been.calledWithMatch({ foo:'bar', i:3 }) .and.to.have.returned(sinon.match({ attributes: { j: 3, i: 3, foo: 'bar' } })); expect(getAttributes(scratch.firstElementChild)).to.eql({ j: '3', i: '3', foo: 'bar' }); }); it('should re-render nested components', () => { let doRender = null, alt = false; class Outer extends Component { componentDidMount() { let i = 1; doRender = () => this.setState({ i: ++i }); } componentWillUnmount() {} render(props, { i }) { if (alt) return
; return ; } } sinon.spy(Outer.prototype, 'render'); sinon.spy(Outer.prototype, 'componentDidMount'); sinon.spy(Outer.prototype, 'componentWillUnmount'); let j = 0; class Inner extends Component { constructor(...args) { super(); this._constructor(...args); } _constructor() {} componentWillMount() {} componentDidMount() {} componentWillUnmount() {} render(props) { return
inner
; } } sinon.spy(Inner.prototype, '_constructor'); sinon.spy(Inner.prototype, 'render'); sinon.spy(Inner.prototype, 'componentWillMount'); sinon.spy(Inner.prototype, 'componentDidMount'); sinon.spy(Inner.prototype, 'componentWillUnmount'); render(, scratch); expect(Outer.prototype.componentDidMount).to.have.been.calledOnce; // update & flush doRender(); rerender(); expect(Outer.prototype.componentWillUnmount).not.to.have.been.called; expect(Inner.prototype._constructor).to.have.been.calledOnce; expect(Inner.prototype.componentWillUnmount).not.to.have.been.called; expect(Inner.prototype.componentWillMount).to.have.been.calledOnce; expect(Inner.prototype.componentDidMount).to.have.been.calledOnce; expect(Inner.prototype.render).to.have.been.calledTwice; expect(Inner.prototype.render.secondCall) .to.have.been.calledWithMatch({ foo:'bar', i:2 }) .and.to.have.returned(sinon.match({ attributes: { j: 2, i: 2, foo: 'bar' } })); expect(getAttributes(scratch.firstElementChild)).to.eql({ j: '2', i: '2', foo: 'bar' }); expect(sortAttributes(scratch.innerHTML)).to.equal(sortAttributes('
inner
')); // update & flush doRender(); rerender(); expect(Inner.prototype.componentWillUnmount).not.to.have.been.called; expect(Inner.prototype.componentWillMount).to.have.been.calledOnce; expect(Inner.prototype.componentDidMount).to.have.been.calledOnce; expect(Inner.prototype.render).to.have.been.calledThrice; expect(Inner.prototype.render.thirdCall) .to.have.been.calledWithMatch({ foo:'bar', i:3 }) .and.to.have.returned(sinon.match({ attributes: { j: 3, i: 3, foo: 'bar' } })); expect(getAttributes(scratch.firstElementChild)).to.eql({ j: '3', i: '3', foo: 'bar' }); // update & flush alt = true; doRender(); rerender(); expect(Inner.prototype.componentWillUnmount).to.have.been.calledOnce; expect(scratch.innerHTML).to.equal('
'); // update & flush alt = false; doRender(); rerender(); expect(sortAttributes(scratch.innerHTML)).to.equal(sortAttributes('
inner
')); }); it('should resolve intermediary functional component', () => { let ctx = {}; class Root extends Component { getChildContext() { return { ctx }; } render() { return ; } } const Func = sinon.spy( () => ); class Inner extends Component { componentWillMount() {} componentDidMount() {} componentWillUnmount() {} render() { return
inner
; } } spyAll(Inner.prototype); let root = render(, scratch); expect(Inner.prototype.componentWillMount).to.have.been.calledOnce; expect(Inner.prototype.componentDidMount).to.have.been.calledOnce; expect(Inner.prototype.componentWillMount).to.have.been.calledBefore(Inner.prototype.componentDidMount); render(, scratch, root); expect(Inner.prototype.componentWillUnmount).to.have.been.calledOnce; }); it('should unmount children of high-order components without unmounting parent', () => { let outer, inner2, counter=0; class Outer extends Component { constructor(props, context) { super(props, context); outer = this; this.state = { child: this.props.child }; } componentWillUnmount(){} componentWillMount(){} componentDidMount(){} render(_, { child:C }) { return ; } } spyAll(Outer.prototype); class Inner extends Component { componentWillUnmount(){} componentWillMount(){} componentDidMount(){} render() { return h('element'+(++counter)); } } spyAll(Inner.prototype); class Inner2 extends Component { constructor(props, context) { super(props, context); inner2 = this; } componentWillUnmount(){} componentWillMount(){} componentDidMount(){} render() { return h('element'+(++counter)); } } spyAll(Inner2.prototype); render(, scratch); // outer should only have been mounted once expect(Outer.prototype.componentWillMount, 'outer initial').to.have.been.calledOnce; expect(Outer.prototype.componentDidMount, 'outer initial').to.have.been.calledOnce; expect(Outer.prototype.componentWillUnmount, 'outer initial').not.to.have.been.called; // inner should only have been mounted once expect(Inner.prototype.componentWillMount, 'inner initial').to.have.been.calledOnce; expect(Inner.prototype.componentDidMount, 'inner initial').to.have.been.calledOnce; expect(Inner.prototype.componentWillUnmount, 'inner initial').not.to.have.been.called; outer.setState({ child:Inner2 }); outer.forceUpdate(); expect(Inner2.prototype.render).to.have.been.calledOnce; // outer should still only have been mounted once expect(Outer.prototype.componentWillMount, 'outer swap').to.have.been.calledOnce; expect(Outer.prototype.componentDidMount, 'outer swap').to.have.been.calledOnce; expect(Outer.prototype.componentWillUnmount, 'outer swap').not.to.have.been.called; // inner should only have been mounted once expect(Inner2.prototype.componentWillMount, 'inner2 swap').to.have.been.calledOnce; expect(Inner2.prototype.componentDidMount, 'inner2 swap').to.have.been.calledOnce; expect(Inner2.prototype.componentWillUnmount, 'inner2 swap').not.to.have.been.called; inner2.forceUpdate(); expect(Inner2.prototype.render, 'inner2 update').to.have.been.calledTwice; expect(Inner2.prototype.componentWillMount, 'inner2 update').to.have.been.calledOnce; expect(Inner2.prototype.componentDidMount, 'inner2 update').to.have.been.calledOnce; expect(Inner2.prototype.componentWillUnmount, 'inner2 update').not.to.have.been.called; }); it('should remount when swapping between HOC child types', () => { class Outer extends Component { render({ child: Child }) { return ; } } class Inner extends Component { componentWillMount() {} componentWillUnmount() {} render() { return
foo
; } } spyAll(Inner.prototype); const InnerFunc = () => (
bar
); let root = render(, scratch, root); expect(Inner.prototype.componentWillMount, 'initial mount').to.have.been.calledOnce; expect(Inner.prototype.componentWillUnmount, 'initial mount').not.to.have.been.called; Inner.prototype.componentWillMount.reset(); root = render(, scratch, root); expect(Inner.prototype.componentWillMount, 'unmount').not.to.have.been.called; expect(Inner.prototype.componentWillUnmount, 'unmount').to.have.been.calledOnce; Inner.prototype.componentWillUnmount.reset(); root = render(, scratch, root); expect(Inner.prototype.componentWillMount, 'remount').to.have.been.calledOnce; expect(Inner.prototype.componentWillUnmount, 'remount').not.to.have.been.called; }); }); describe('Component Nesting', () => { let useIntermediary = false; let createComponent = (Intermediary) => { class C extends Component { componentWillMount() {} render({ children }) { if (!useIntermediary) return children[0]; let I = useIntermediary===true ? Intermediary : useIntermediary; return {children}; } } spyAll(C.prototype); return C; }; let createFunction = () => sinon.spy( ({ children }) => children[0] ); let root; let rndr = n => root = render(n, scratch, root); let F1 = createFunction(); let F2 = createFunction(); let F3 = createFunction(); let C1 = createComponent(F1); let C2 = createComponent(F2); let C3 = createComponent(F3); let reset = () => [C1, C2, C3].reduce( (acc, c) => acc.concat( Object.keys(c.prototype).map(key => c.prototype[key]) ), [F1, F2, F3] ).forEach( c => c.reset && c.reset() ); it('should handle lifecycle for no intermediary in component tree', () => { reset(); rndr(Some Text); expect(C1.prototype.componentWillMount, 'initial mount').to.have.been.calledOnce; expect(C2.prototype.componentWillMount, 'initial mount').to.have.been.calledOnce; expect(C3.prototype.componentWillMount, 'initial mount').to.have.been.calledOnce; reset(); rndr(Some Text); expect(C1.prototype.componentWillMount, 'unmount innermost, C1').not.to.have.been.called; expect(C2.prototype.componentWillMount, 'unmount innermost, C2').not.to.have.been.called; reset(); rndr(Some Text); expect(C1.prototype.componentWillMount, 'swap innermost').not.to.have.been.called; expect(C3.prototype.componentWillMount, 'swap innermost').to.have.been.calledOnce; reset(); rndr(Some Text); expect(C1.prototype.componentWillMount, 'inject between, C1').not.to.have.been.called; expect(C2.prototype.componentWillMount, 'inject between, C2').to.have.been.calledOnce; expect(C3.prototype.componentWillMount, 'inject between, C3').to.have.been.calledOnce; }); it('should handle lifecycle for nested intermediary functional components', () => { useIntermediary = true; rndr(
); reset(); rndr(Some Text); expect(C1.prototype.componentWillMount, 'initial mount w/ intermediary fn, C1').to.have.been.calledOnce; expect(C2.prototype.componentWillMount, 'initial mount w/ intermediary fn, C2').to.have.been.calledOnce; expect(C3.prototype.componentWillMount, 'initial mount w/ intermediary fn, C3').to.have.been.calledOnce; reset(); rndr(Some Text); expect(C1.prototype.componentWillMount, 'unmount innermost w/ intermediary fn, C1').not.to.have.been.called; expect(C2.prototype.componentWillMount, 'unmount innermost w/ intermediary fn, C2').not.to.have.been.called; reset(); rndr(Some Text); expect(C1.prototype.componentWillMount, 'swap innermost w/ intermediary fn').not.to.have.been.called; expect(C3.prototype.componentWillMount, 'swap innermost w/ intermediary fn').to.have.been.calledOnce; reset(); rndr(Some Text); expect(C1.prototype.componentWillMount, 'inject between, C1 w/ intermediary fn').not.to.have.been.called; expect(C2.prototype.componentWillMount, 'inject between, C2 w/ intermediary fn').to.have.been.calledOnce; expect(C3.prototype.componentWillMount, 'inject between, C3 w/ intermediary fn').to.have.been.calledOnce; }); it('should handle lifecycle for nested intermediary elements', () => { useIntermediary = 'div'; rndr(
); reset(); rndr(Some Text); expect(C1.prototype.componentWillMount, 'initial mount w/ intermediary div, C1').to.have.been.calledOnce; expect(C2.prototype.componentWillMount, 'initial mount w/ intermediary div, C2').to.have.been.calledOnce; expect(C3.prototype.componentWillMount, 'initial mount w/ intermediary div, C3').to.have.been.calledOnce; reset(); rndr(Some Text); expect(C1.prototype.componentWillMount, 'unmount innermost w/ intermediary div, C1').not.to.have.been.called; expect(C2.prototype.componentWillMount, 'unmount innermost w/ intermediary div, C2').not.to.have.been.called; reset(); rndr(Some Text); expect(C1.prototype.componentWillMount, 'swap innermost w/ intermediary div').not.to.have.been.called; expect(C3.prototype.componentWillMount, 'swap innermost w/ intermediary div').to.have.been.calledOnce; reset(); rndr(Some Text); expect(C1.prototype.componentWillMount, 'inject between, C1 w/ intermediary div').not.to.have.been.called; expect(C2.prototype.componentWillMount, 'inject between, C2 w/ intermediary div').to.have.been.calledOnce; expect(C3.prototype.componentWillMount, 'inject between, C3 w/ intermediary div').to.have.been.calledOnce; }); }); }); preact-8.2.5/test/browser/context.js000066400000000000000000000126161315110146500174730ustar00rootroot00000000000000import { h, render, Component } from '../../src/preact'; /** @jsx h */ const CHILDREN_MATCHER = sinon.match( v => v==null || Array.isArray(v) && !v.length , '[empty children]'); describe('context', () => { let scratch; before( () => { scratch = document.createElement('div'); (document.body || document.documentElement).appendChild(scratch); }); beforeEach( () => { scratch.innerHTML = ''; }); after( () => { scratch.parentNode.removeChild(scratch); scratch = null; }); it('should pass context to grandchildren', () => { const CONTEXT = { a:'a' }; const PROPS = { b:'b' }; // let inner; class Outer extends Component { getChildContext() { return CONTEXT; } render(props) { return
; } } sinon.spy(Outer.prototype, 'getChildContext'); class Inner extends Component { // constructor() { // super(); // inner = this; // } shouldComponentUpdate() { return true; } componentWillReceiveProps() {} componentWillUpdate() {} componentDidUpdate() {} render(props, state, context) { return
{ context && context.a }
; } } sinon.spy(Inner.prototype, 'shouldComponentUpdate'); sinon.spy(Inner.prototype, 'componentWillReceiveProps'); sinon.spy(Inner.prototype, 'componentWillUpdate'); sinon.spy(Inner.prototype, 'componentDidUpdate'); sinon.spy(Inner.prototype, 'render'); render(, scratch, scratch.lastChild); expect(Outer.prototype.getChildContext).to.have.been.calledOnce; // initial render does not invoke anything but render(): expect(Inner.prototype.render).to.have.been.calledWith({ children:CHILDREN_MATCHER }, {}, CONTEXT); CONTEXT.foo = 'bar'; render(, scratch, scratch.lastChild); expect(Outer.prototype.getChildContext).to.have.been.calledTwice; let props = { children: CHILDREN_MATCHER, ...PROPS }; expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(props, {}, CONTEXT); expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(props, CONTEXT); expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(props, {}); expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({ children:CHILDREN_MATCHER }, {}); expect(Inner.prototype.render).to.have.been.calledWith(props, {}, CONTEXT); /* Future: * Newly created context objects are *not* currently cloned. * This test checks that they *are* cloned. */ // Inner.prototype.render.reset(); // CONTEXT.foo = 'baz'; // inner.forceUpdate(); // expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, { a:'a', foo:'bar' }); }); it('should pass context to direct children', () => { const CONTEXT = { a:'a' }; const PROPS = { b:'b' }; class Outer extends Component { getChildContext() { return CONTEXT; } render(props) { return ; } } sinon.spy(Outer.prototype, 'getChildContext'); class Inner extends Component { shouldComponentUpdate() { return true; } componentWillReceiveProps() {} componentWillUpdate() {} componentDidUpdate() {} render(props, state, context) { return
{ context && context.a }
; } } sinon.spy(Inner.prototype, 'shouldComponentUpdate'); sinon.spy(Inner.prototype, 'componentWillReceiveProps'); sinon.spy(Inner.prototype, 'componentWillUpdate'); sinon.spy(Inner.prototype, 'componentDidUpdate'); sinon.spy(Inner.prototype, 'render'); render(, scratch, scratch.lastChild); expect(Outer.prototype.getChildContext).to.have.been.calledOnce; // initial render does not invoke anything but render(): expect(Inner.prototype.render).to.have.been.calledWith({ children: CHILDREN_MATCHER }, {}, CONTEXT); CONTEXT.foo = 'bar'; render(, scratch, scratch.lastChild); expect(Outer.prototype.getChildContext).to.have.been.calledTwice; let props = { children: CHILDREN_MATCHER, ...PROPS }; expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(props, {}, CONTEXT); expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(props, CONTEXT); expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(props, {}); expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({ children: CHILDREN_MATCHER }, {}); expect(Inner.prototype.render).to.have.been.calledWith(props, {}, CONTEXT); // make sure render() could make use of context.a expect(Inner.prototype.render).to.have.returned(sinon.match({ children:['a'] })); }); it('should preserve existing context properties when creating child contexts', () => { let outerContext = { outer:true }, innerContext = { inner:true }; class Outer extends Component { getChildContext() { return { outerContext }; } render() { return
; } } class Inner extends Component { getChildContext() { return { innerContext }; } render() { return ; } } class InnerMost extends Component { render() { return test; } } sinon.spy(Inner.prototype, 'render'); sinon.spy(InnerMost.prototype, 'render'); render(, scratch); expect(Inner.prototype.render).to.have.been.calledWith({ children: CHILDREN_MATCHER }, {}, { outerContext }); expect(InnerMost.prototype.render).to.have.been.calledWith({ children: CHILDREN_MATCHER }, {}, { outerContext, innerContext }); }); }); preact-8.2.5/test/browser/devtools.js000066400000000000000000000176551315110146500176560ustar00rootroot00000000000000import { h, Component, render } from '../../src/preact'; import { initDevTools } from '../../devtools/devtools'; import { unmountComponent } from '../../src/vdom/component'; class StatefulComponent extends Component { constructor(props) { super(props); this.state = {count: 0}; } render() { return h('span', {}, String(this.state.count)); } } function FunctionalComponent() { return h('span', {class: 'functional'}, 'Functional'); } function Label({label}) { return label; } class MultiChild extends Component { constructor(props) { super(props); this.state = {count: props.initialCount}; } render() { return h('div', {}, Array(this.state.count).fill('child')); } } let describe_ = describe; if (!('name' in Function.prototype)) { // Skip these tests under Internet Explorer describe_ = describe.skip; } describe_('React Developer Tools integration', () => { let cleanup; let container; let renderer; // Maps of DOM node to React*Component-like objects. // For composite components, there will be two instances for each node, one // for the composite component (instanceMap) and one for the root child DOM // component rendered by that component (domInstanceMap) let instanceMap = new Map(); let domInstanceMap = new Map(); beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); const onMount = instance => { if (instance._renderedChildren) { domInstanceMap.set(instance.node, instance); } else { instanceMap.set(instance.node, instance); } }; const onUnmount = instance => { instanceMap.delete(instance.node); domInstanceMap.delete(instance.node); }; global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = { inject: sinon.spy(_renderer => { renderer = _renderer; renderer.Mount._renderNewRootComponent = sinon.stub(); renderer.Reconciler.mountComponent = sinon.spy(onMount); renderer.Reconciler.unmountComponent = sinon.spy(onUnmount); renderer.Reconciler.receiveComponent = sinon.stub(); }) }; cleanup = initDevTools(); }); afterEach(() => { container.remove(); cleanup(); }); it('registers preact as a renderer with the React DevTools hook', () => { expect(global.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject).to.be.called; }); // Basic component addition/update/removal tests it('notifies dev tools about new components', () => { render(h(StatefulComponent), container); expect(renderer.Reconciler.mountComponent).to.be.called; }); it('notifies dev tools about component updates', () => { const node = render(h(StatefulComponent), container); node._component.forceUpdate(); expect(renderer.Reconciler.receiveComponent).to.be.called; }); it('notifies dev tools when components are removed', () => { const node = render(h(StatefulComponent), container); unmountComponent(node._component, true); expect(renderer.Reconciler.unmountComponent).to.be.called; }); // Test properties of DOM components exposed to devtools via // ReactDOMComponent-like instances it('exposes the tag name of DOM components', () => { const node = render(h(StatefulComponent), container); const domInstance = domInstanceMap.get(node); expect(domInstance._currentElement.type).to.equal('span'); }); it('exposes DOM component props', () => { const node = render(h(FunctionalComponent), container); const domInstance = domInstanceMap.get(node); expect(domInstance._currentElement.props.class).to.equal('functional'); }); it('exposes text component contents', () => { const node = render(h(Label, {label: 'Text content'}), container); const textInstance = domInstanceMap.get(node); expect(textInstance._stringText).to.equal('Text content'); }); // Test properties of composite components exposed to devtools via // ReactCompositeComponent-like instances it('exposes the name of composite component classes', () => { const node = render(h(StatefulComponent), container); expect(instanceMap.get(node).getName()).to.equal('StatefulComponent'); }); it('exposes composite component props', () => { const node = render(h(Label, {label: 'Text content'}), container); const instance = instanceMap.get(node); expect(instance._currentElement.props.label).to.equal('Text content'); }); it('exposes composite component state', () => { const node = render(h(StatefulComponent), container); node._component.setState({count: 42}); node._component.forceUpdate(); expect(instanceMap.get(node).state).to.deep.equal({count: 42}); }); // Test setting state via devtools it('updates component when setting state from devtools', () => { const node = render(h(StatefulComponent), container); instanceMap.get(node).setState({count: 10}); instanceMap.get(node).forceUpdate(); expect(node.textContent).to.equal('10'); }); // Test that the original instance is exposed via `_instance` so it can // be accessed conveniently via `$r` in devtools // Functional component handling tests xit('wraps functional components with stateful ones', () => { const vnode = h(FunctionalComponent); expect(vnode.nodeName.prototype).to.have.property('render'); }); it('exposes the name of functional components', () => { const node = render(h(FunctionalComponent), container); const instance = instanceMap.get(node); expect(instance.getName()).to.equal('FunctionalComponent'); }); xit('exposes a fallback name if the component has no useful name', () => { const node = render(h(() => h('div')), container); const instance = instanceMap.get(node); expect(instance.getName()).to.equal('(Function.name missing)'); }); // Test handling of DOM children it('notifies dev tools about DOM children', () => { const node = render(h(StatefulComponent), container); const domInstance = domInstanceMap.get(node); expect(renderer.Reconciler.mountComponent).to.have.been.calledWith(domInstance); }); it('notifies dev tools when a component update adds DOM children', () => { const node = render(h(MultiChild, {initialCount: 2}), container); node._component.setState({count: 4}); node._component.forceUpdate(); expect(renderer.Reconciler.mountComponent).to.have.been.called.twice; }); it('notifies dev tools when a component update modifies DOM children', () => { const node = render(h(StatefulComponent), container); instanceMap.get(node).setState({count: 10}); instanceMap.get(node).forceUpdate(); const textInstance = domInstanceMap.get(node.childNodes[0]); expect(textInstance._stringText).to.equal('10'); }); it('notifies dev tools when a component update removes DOM children', () => { const node = render(h(MultiChild, {initialCount: 1}), container); node._component.setState({count: 0}); node._component.forceUpdate(); expect(renderer.Reconciler.unmountComponent).to.be.called; }); // Root component info it('exposes root components on the _instancesByReactRootID map', () => { render(h(StatefulComponent), container); expect(Object.keys(renderer.Mount._instancesByReactRootID).length).to.equal(1); }); it('notifies dev tools when new root components are mounted', () => { render(h(StatefulComponent), container); expect(renderer.Mount._renderNewRootComponent).to.be.called; }); it('removes root components when they are unmounted', () => { const node = render(h(StatefulComponent), container); unmountComponent(node._component, true); expect(Object.keys(renderer.Mount._instancesByReactRootID).length).to.equal(0); }); it('counts root components correctly when a root renders a composite child', () => { function Child() { return h('main'); } function Parent() { return h(Child); } render(h(Parent), container); expect(Object.keys(renderer.Mount._instancesByReactRootID).length).to.equal(1); }); it('counts root components correctly when a native element has a composite child', () => { function Link() { return h('a'); } function Root() { return h('div', {}, h(Link)); } render(h(Root), container); expect(Object.keys(renderer.Mount._instancesByReactRootID).length).to.equal(1); }); }); preact-8.2.5/test/browser/keys.js000066400000000000000000000043461315110146500167630ustar00rootroot00000000000000import { h, Component, render } from '../../src/preact'; /** @jsx h */ describe('keys', () => { let scratch; before( () => { scratch = document.createElement('div'); (document.body || document.documentElement).appendChild(scratch); }); beforeEach( () => { scratch.innerHTML = ''; }); after( () => { scratch.parentNode.removeChild(scratch); scratch = null; }); // See developit/preact-compat#21 it('should remove orphaned keyed nodes', () => { const root = render((
1
  • a
  • b
  • ), scratch); render((
    2
  • b
  • c
  • ), scratch, root); expect(scratch.innerHTML).to.equal('
    2
  • b
  • c
  • '); }); it('should set VNode#key property', () => { expect(
    ).to.have.property('key').that.is.empty; expect(
    ).to.have.property('key').that.is.empty; expect(
    ).to.have.property('key', '1'); }); it('should remove keyed nodes (#232)', () => { class App extends Component { componentDidMount() { setTimeout(() => this.setState({opened: true,loading: true}), 10); setTimeout(() => this.setState({opened: true,loading: false}), 20); } render({ opened, loading }) { return (
    This div needs to be here for this to break
    { opened && !loading &&
    {[]}
    }
    ); } } class BusyIndicator extends Component { render({ children, busy }) { return
    { children && children.length ? children :
    }
    indicator
    indicator
    indicator
    ; } } let root; root = render(, scratch, root); root = render(, scratch, root); root = render(, scratch, root); let html = String(root.innerHTML).replace(/ class=""/g, ''); expect(html).to.equal('
    This div needs to be here for this to break
    indicator
    indicator
    indicator
    '); }); }); preact-8.2.5/test/browser/lifecycle.js000066400000000000000000000400131315110146500177360ustar00rootroot00000000000000import { h, render, rerender, Component } from '../../src/preact'; /** @jsx h */ let spyAll = obj => Object.keys(obj).forEach( key => sinon.spy(obj,key) ); const EMPTY_CHILDREN = []; describe('Lifecycle methods', () => { let scratch; before( () => { scratch = document.createElement('div'); (document.body || document.documentElement).appendChild(scratch); }); beforeEach( () => { scratch.innerHTML = ''; }); after( () => { scratch.parentNode.removeChild(scratch); scratch = null; }); describe('#componentWillUpdate', () => { it('should NOT be called on initial render', () => { class ReceivePropsComponent extends Component { componentWillUpdate() {} render() { return
    ; } } sinon.spy(ReceivePropsComponent.prototype, 'componentWillUpdate'); render(, scratch); expect(ReceivePropsComponent.prototype.componentWillUpdate).not.to.have.been.called; }); it('should be called when rerender with new props from parent', () => { let doRender; class Outer extends Component { constructor(p, c) { super(p, c); this.state = { i: 0 }; } componentDidMount() { doRender = () => this.setState({ i: this.state.i + 1 }); } render(props, { i }) { return ; } } class Inner extends Component { componentWillUpdate(nextProps, nextState) { expect(nextProps).to.be.deep.equal({ children:EMPTY_CHILDREN, i: 1 }); expect(nextState).to.be.deep.equal({}); } render() { return
    ; } } sinon.spy(Inner.prototype, 'componentWillUpdate'); sinon.spy(Outer.prototype, 'componentDidMount'); // Initial render render(, scratch); expect(Inner.prototype.componentWillUpdate).not.to.have.been.called; // Rerender inner with new props doRender(); rerender(); expect(Inner.prototype.componentWillUpdate).to.have.been.called; }); it('should be called on new state', () => { let doRender; class ReceivePropsComponent extends Component { componentWillUpdate() {} componentDidMount() { doRender = () => this.setState({ i: this.state.i + 1 }); } render() { return
    ; } } sinon.spy(ReceivePropsComponent.prototype, 'componentWillUpdate'); render(, scratch); expect(ReceivePropsComponent.prototype.componentWillUpdate).not.to.have.been.called; doRender(); rerender(); expect(ReceivePropsComponent.prototype.componentWillUpdate).to.have.been.called; }); it('should be called after children are mounted', () => { let log = []; class Inner extends Component { componentDidMount() { log.push('Inner mounted'); // Verify that the component is actually mounted when this // callback is invoked. expect(scratch.querySelector('#inner')).to.equal(this.base); } render() { return
    ; } } class Outer extends Component { componentDidUpdate() { log.push('Outer updated'); } render(props) { return props.renderInner ? :
    ; } } const elem = render(, scratch); render(, scratch, elem); // expect(log).to.deep.equal(['Inner mounted', 'Outer updated']); }); }); describe('#componentWillReceiveProps', () => { it('should NOT be called on initial render', () => { class ReceivePropsComponent extends Component { componentWillReceiveProps() {} render() { return
    ; } } sinon.spy(ReceivePropsComponent.prototype, 'componentWillReceiveProps'); render(, scratch); expect(ReceivePropsComponent.prototype.componentWillReceiveProps).not.to.have.been.called; }); it('should be called when rerender with new props from parent', () => { let doRender; class Outer extends Component { constructor(p, c) { super(p, c); this.state = { i: 0 }; } componentDidMount() { doRender = () => this.setState({ i: this.state.i + 1 }); } render(props, { i }) { return ; } } class Inner extends Component { componentWillMount() { expect(this.props.i).to.be.equal(0); } componentWillReceiveProps(nextProps) { expect(nextProps.i).to.be.equal(1); } render() { return
    ; } } sinon.spy(Inner.prototype, 'componentWillReceiveProps'); sinon.spy(Outer.prototype, 'componentDidMount'); // Initial render render(, scratch); expect(Inner.prototype.componentWillReceiveProps).not.to.have.been.called; // Rerender inner with new props doRender(); rerender(); expect(Inner.prototype.componentWillReceiveProps).to.have.been.called; }); it('should be called in right execution order', () => { let doRender; class Outer extends Component { constructor(p, c) { super(p, c); this.state = { i: 0 }; } componentDidMount() { doRender = () => this.setState({ i: this.state.i + 1 }); } render(props, { i }) { return ; } } class Inner extends Component { componentDidUpdate() { expect(Inner.prototype.componentWillReceiveProps).to.have.been.called; expect(Inner.prototype.componentWillUpdate).to.have.been.called; } componentWillReceiveProps() { expect(Inner.prototype.componentWillUpdate).not.to.have.been.called; expect(Inner.prototype.componentDidUpdate).not.to.have.been.called; } componentWillUpdate() { expect(Inner.prototype.componentWillReceiveProps).to.have.been.called; expect(Inner.prototype.componentDidUpdate).not.to.have.been.called; } render() { return
    ; } } sinon.spy(Inner.prototype, 'componentWillReceiveProps'); sinon.spy(Inner.prototype, 'componentDidUpdate'); sinon.spy(Inner.prototype, 'componentWillUpdate'); sinon.spy(Outer.prototype, 'componentDidMount'); render(, scratch); doRender(); rerender(); expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledBefore(Inner.prototype.componentWillUpdate); expect(Inner.prototype.componentWillUpdate).to.have.been.calledBefore(Inner.prototype.componentDidUpdate); }); }); describe('top-level componentWillUnmount', () => { it('should invoke componentWillUnmount for top-level components', () => { class Foo extends Component { componentDidMount() {} componentWillUnmount() {} } class Bar extends Component { componentDidMount() {} componentWillUnmount() {} } spyAll(Foo.prototype); spyAll(Bar.prototype); render(, scratch, scratch.lastChild); expect(Foo.prototype.componentDidMount, 'initial render').to.have.been.calledOnce; render(, scratch, scratch.lastChild); expect(Foo.prototype.componentWillUnmount, 'when replaced').to.have.been.calledOnce; expect(Bar.prototype.componentDidMount, 'when replaced').to.have.been.calledOnce; render(
    , scratch, scratch.lastChild); expect(Bar.prototype.componentWillUnmount, 'when removed').to.have.been.calledOnce; }); }); let _it = it; describe('#constructor and component(Did|Will)(Mount|Unmount)', () => { /* global DISABLE_FLAKEY */ let it = DISABLE_FLAKEY ? xit : _it; let setState; class Outer extends Component { constructor(p, c) { super(p, c); this.state = { show:true }; setState = s => this.setState(s); } render(props, { show }) { return (
    { show && ( ) }
    ); } } class LifecycleTestComponent extends Component { constructor(p, c) { super(p, c); this._constructor(); } _constructor() {} componentWillMount() {} componentDidMount() {} componentWillUnmount() {} render() { return
    ; } } class Inner extends LifecycleTestComponent { render() { return (
    ); } } class InnerMost extends LifecycleTestComponent { render() { return
    ; } } let spies = ['_constructor', 'componentWillMount', 'componentDidMount', 'componentWillUnmount']; let verifyLifycycleMethods = (TestComponent) => { let proto = TestComponent.prototype; spies.forEach( s => sinon.spy(proto, s) ); let reset = () => spies.forEach( s => proto[s].reset() ); it('should be invoked for components on initial render', () => { reset(); render(, scratch); expect(proto._constructor).to.have.been.called; expect(proto.componentDidMount).to.have.been.called; expect(proto.componentWillMount).to.have.been.calledBefore(proto.componentDidMount); expect(proto.componentDidMount).to.have.been.called; }); it('should be invoked for components on unmount', () => { reset(); setState({ show:false }); rerender(); expect(proto.componentWillUnmount).to.have.been.called; }); it('should be invoked for components on re-render', () => { reset(); setState({ show:true }); rerender(); expect(proto._constructor).to.have.been.called; expect(proto.componentDidMount).to.have.been.called; expect(proto.componentWillMount).to.have.been.calledBefore(proto.componentDidMount); expect(proto.componentDidMount).to.have.been.called; }); }; describe('inner components', () => { verifyLifycycleMethods(Inner); }); describe('innermost components', () => { verifyLifycycleMethods(InnerMost); }); describe('when shouldComponentUpdate() returns false', () => { let setState; class Outer extends Component { constructor() { super(); this.state = { show:true }; setState = s => this.setState(s); } render(props, { show }) { return (
    { show && (
    ) }
    ); } } class Inner extends Component { shouldComponentUpdate(){ return false; } componentWillMount() {} componentDidMount() {} componentWillUnmount() {} render() { return
    ; } } let proto = Inner.prototype; let spies = ['componentWillMount', 'componentDidMount', 'componentWillUnmount']; spies.forEach( s => sinon.spy(proto, s) ); let reset = () => spies.forEach( s => proto[s].reset() ); beforeEach( () => reset() ); it('should be invoke normally on initial mount', () => { render(, scratch); expect(proto.componentWillMount).to.have.been.called; expect(proto.componentWillMount).to.have.been.calledBefore(proto.componentDidMount); expect(proto.componentDidMount).to.have.been.called; }); it('should be invoked normally on unmount', () => { setState({ show:false }); rerender(); expect(proto.componentWillUnmount).to.have.been.called; }); it('should still invoke mount for shouldComponentUpdate():false', () => { setState({ show:true }); rerender(); expect(proto.componentWillMount).to.have.been.called; expect(proto.componentWillMount).to.have.been.calledBefore(proto.componentDidMount); expect(proto.componentDidMount).to.have.been.called; }); it('should still invoke unmount for shouldComponentUpdate():false', () => { setState({ show:false }); rerender(); expect(proto.componentWillUnmount).to.have.been.called; }); }); }); describe('shouldComponentUpdate', () => { let setState; class Should extends Component { constructor() { super(); this.state = { show:true }; setState = s => this.setState(s); } render(props, { show }) { return show ?
    : null; } } class ShouldNot extends Should { shouldComponentUpdate() { return false; } } sinon.spy(Should.prototype, 'render'); sinon.spy(ShouldNot.prototype, 'shouldComponentUpdate'); beforeEach(() => Should.prototype.render.reset()); it('should rerender component on change by default', () => { render(, scratch); setState({ show:false }); rerender(); expect(Should.prototype.render).to.have.been.calledTwice; }); it('should not rerender component if shouldComponentUpdate returns false', () => { render(, scratch); setState({ show:false }); rerender(); expect(ShouldNot.prototype.shouldComponentUpdate).to.have.been.calledOnce; expect(ShouldNot.prototype.render).to.have.been.calledOnce; }); }); describe('Lifecycle DOM Timing', () => { it('should be invoked when dom does (DidMount, WillUnmount) or does not (WillMount, DidUnmount) exist', () => { let setState; class Outer extends Component { constructor() { super(); this.state = { show:true }; setState = s => { this.setState(s); this.forceUpdate(); }; } componentWillMount() { expect(document.getElementById('OuterDiv'), 'Outer componentWillMount').to.not.exist; } componentDidMount() { expect(document.getElementById('OuterDiv'), 'Outer componentDidMount').to.exist; } componentWillUnmount() { expect(document.getElementById('OuterDiv'), 'Outer componentWillUnmount').to.exist; setTimeout( () => { expect(document.getElementById('OuterDiv'), 'Outer after componentWillUnmount').to.not.exist; }, 0); } render(props, { show }) { return (
    { show && (
    ) }
    ); } } class Inner extends Component { componentWillMount() { expect(document.getElementById('InnerDiv'), 'Inner componentWillMount').to.not.exist; } componentDidMount() { expect(document.getElementById('InnerDiv'), 'Inner componentDidMount').to.exist; } componentWillUnmount() { // @TODO Component mounted into elements (non-components) // are currently unmounted after those elements, so their // DOM is unmounted prior to the method being called. //expect(document.getElementById('InnerDiv'), 'Inner componentWillUnmount').to.exist; setTimeout( () => { expect(document.getElementById('InnerDiv'), 'Inner after componentWillUnmount').to.not.exist; }, 0); } render() { return
    ; } } let proto = Inner.prototype; let spies = ['componentWillMount', 'componentDidMount', 'componentWillUnmount']; spies.forEach( s => sinon.spy(proto, s) ); let reset = () => spies.forEach( s => proto[s].reset() ); render(, scratch); expect(proto.componentWillMount).to.have.been.called; expect(proto.componentWillMount).to.have.been.calledBefore(proto.componentDidMount); expect(proto.componentDidMount).to.have.been.called; reset(); setState({ show:false }); expect(proto.componentWillUnmount).to.have.been.called; reset(); setState({ show:true }); expect(proto.componentWillMount).to.have.been.called; expect(proto.componentWillMount).to.have.been.calledBefore(proto.componentDidMount); expect(proto.componentDidMount).to.have.been.called; }); it('should remove this.base for HOC', () => { let createComponent = (name, fn) => { class C extends Component { componentWillUnmount() { expect(this.base, `${name}.componentWillUnmount`).to.exist; setTimeout( () => { expect(this.base, `after ${name}.componentWillUnmount`).not.to.exist; }, 0); } render(props) { return fn(props); } } spyAll(C.prototype); return C; }; class Wrapper extends Component { render({ children }) { return
    {children}
    ; } } let One = createComponent('One', () => one ); let Two = createComponent('Two', () => two ); let Three = createComponent('Three', () => three ); let components = [One, Two, Three]; let Selector = createComponent('Selector', ({ page }) => { let Child = components[page]; return Child && ; }); class App extends Component { render(_, { page }) { return ; } } let app; render( app=c } />, scratch); for (let i=0; i<20; i++) { app.setState({ page: i%components.length }); app.forceUpdate(); } }); }); }); preact-8.2.5/test/browser/performance.js000066400000000000000000000226211315110146500203050ustar00rootroot00000000000000/*global coverage, ENABLE_PERFORMANCE, NODE_ENV*/ /*eslint no-console:0*/ /** @jsx h */ let { h, Component, render } = require(NODE_ENV==='production' ? '../../dist/preact.min.js' : '../../src/preact'); const MULTIPLIER = ENABLE_PERFORMANCE ? (coverage ? 5 : 1) : 999999; let now = typeof performance!=='undefined' && performance.now ? () => performance.now() : () => +new Date(); function loop(iter, time) { let start = now(), count = 0; while ( now()-start < time ) { count++; iter(); } return count; } function benchmark(iter, callback) { let a = 0; // eslint-disable-line no-unused-vars function noop() { try { a++; } finally { a += Math.random(); } } // warm for (let i=100; i--; ) noop(), iter(); let count = 2, time = 500, passes = 0, noops = loop(noop, time), iterations = 0; function next() { iterations += loop(iter, time); setTimeout(++passes===count ? done : next, 10); } function done() { let ticks = Math.round(noops / iterations * count), hz = iterations / count / time * 1000, message = `${hz|0}/s (${ticks} ticks)`; callback({ iterations, noops, count, time, ticks, hz, message }); } next(); } describe('performance', function() { let scratch; this.timeout(10000); before( () => { if (coverage) { console.warn('WARNING: Code coverage is enabled, which dramatically reduces performance. Do not pay attention to these numbers.'); } scratch = document.createElement('div'); (document.body || document.documentElement).appendChild(scratch); }); beforeEach( () => { scratch.innerHTML = ''; }); after( () => { scratch.parentNode.removeChild(scratch); scratch = null; }); it('should rerender without changes fast', done => { let jsx = (

    a {'b'} c {0} d

    {}}>
    ); let root; benchmark( () => { root = render(jsx, scratch, root); }, ({ ticks, message }) => { console.log(`PERF: empty diff: ${message}`); expect(ticks).to.be.below(150 * MULTIPLIER); done(); }); }); it('should rerender repeated trees fast', done => { class Header extends Component { render() { return (

    a {'b'} c {0} d

    ); } } class Form extends Component { render() { return (
    {}}>
    ); } } class ButtonBar extends Component { render() { return ( ); } } class Button extends Component { render(props) { return
    ); }, ({ ticks, message }) => { console.log(`PERF: large VTree: ${message}`); expect(ticks).to.be.below(2000 * MULTIPLIER); done(); }); }); it('should mutate styles/properties quickly', done => { let counter = 0; const keyLooper = n => c => c % n ? `${c}px` : c; const get = (obj, i) => obj[i%obj.length]; const CLASSES = ['foo', 'foo bar', '', 'baz-bat', null]; const STYLES = []; const MULTIVALUE = ['0 1px', '0 0 1px 0', '0', '1px', '20px 10px', '7em 5px', '1px 0 5em 2px']; const STYLEKEYS = [ ['left', keyLooper(3)], ['top', keyLooper(2)], ['margin', c => get(MULTIVALUE, c).replace('1px', c+'px')], ['padding', c => get(MULTIVALUE, c)], ['position', c => c%5 ? c%2 ? 'absolute' : 'relative' : null], ['display', c => c%10 ? c%2 ? 'block' : 'inline' : 'none'], ['color', c => `rgba(${c%255}, ${255 - c%255}, ${50+c%150}, ${c%50/50})`], ['border', c => c%5 ? `${c%10}px ${c%2?'solid':'dotted'} ${STYLEKEYS[6][1](c)}` : ''] ]; for (let i=0; i<1000; i++) { let style = {}; for (let j=0; j (

    p1

    p2

    p3

    p4

    ); let root, count=0; benchmark( () => { root = render(app(++count), scratch, root); }, ({ ticks, message }) => { console.log(`PERF: style/prop mutation: ${message}`); expect(ticks).to.be.below(350 * MULTIPLIER); done(); }); }); it('should hydrate from SSR quickly', done => { class Header extends Component { render() { return (

    a {'b'} c {0} d

    ); } } class Form extends Component { render() { return (
    {}}>
    ); } } const ButtonBar = () => ( ); class Button extends Component { handleClick() {} render(props) { return
    ), scratch); root.firstElementChild.click(); root.firstElementChild.focus(); expect(click, 'click').to.have.been.calledOnce; if (DISABLE_FLAKEY!==true) { // Focus delegation requires a 50b hack I'm not sure we want to incur expect(focus, 'focus').to.have.been.calledOnce; // IE doesn't set it expect(click).to.have.been.calledWithMatch({ eventPhase: 0 }); // capturing expect(focus).to.have.been.calledWithMatch({ eventPhase: 0 }); // capturing } }); it('should serialize style objects', () => { let root = render((
    test
    ), scratch); let { style } = scratch.childNodes[0]; expect(style).to.have.property('color').that.equals('rgb(255, 255, 255)'); expect(style).to.have.property('background').that.contains('rgb(255, 100, 0)'); expect(style).to.have.property('backgroundPosition').that.equals('10px 10px'); expect(style).to.have.property('backgroundSize', 'cover'); expect(style).to.have.property('padding', '5px'); expect(style).to.have.property('top', '100px'); expect(style).to.have.property('left', '100%'); root = render((
    test
    ), scratch, root); expect(root).to.have.deep.property('style.cssText').that.equals('color: rgb(0, 255, 255);'); root = render((
    test
    ), scratch, root); expect(root).to.have.deep.property('style.cssText').that.equals('display: inline;'); root = render((
    test
    ), scratch, root); expect(root).to.have.deep.property('style.cssText').that.equals('background-color: rgb(0, 255, 255);'); }); it('should support dangerouslySetInnerHTML', () => { let html = 'foo & bar'; let root = render(
    , scratch); expect(scratch.firstChild, 'set').to.have.property('innerHTML', html); expect(scratch.innerHTML).to.equal('
    '+html+'
    '); root = render(
    ab
    , scratch, root); expect(scratch, 'unset').to.have.property('innerHTML', `
    ab
    `); render(
    , scratch, root); expect(scratch.innerHTML, 're-set').to.equal('
    '+html+'
    '); }); it('should apply proper mutation for VNodes with dangerouslySetInnerHTML attr', () => { class Thing extends Component { constructor(props, context) { super(props, context); this.state.html = this.props.html; } render(props, { html }) { return html ?
    :
    ; } } let thing; render( thing=c } html="test" />, scratch); expect(scratch.innerHTML).to.equal('
    test
    '); thing.setState({ html: false }); thing.forceUpdate(); expect(scratch.innerHTML).to.equal('
    '); thing.setState({ html: 'test' }); thing.forceUpdate(); expect(scratch.innerHTML).to.equal('
    test
    '); }); it('should hydrate with dangerouslySetInnerHTML', () => { let html = 'foo & bar'; scratch.innerHTML = `
    ${html}
    `; render(
    , scratch, scratch.lastChild); expect(scratch.firstChild).to.have.property('innerHTML', html); expect(scratch.innerHTML).to.equal(`
    ${html}
    `); }); it('should reconcile mutated DOM attributes', () => { let check = p => render(, scratch, scratch.lastChild), value = () => scratch.lastChild.checked, setValue = p => scratch.lastChild.checked = p; check(true); expect(value()).to.equal(true); check(false); expect(value()).to.equal(false); check(true); expect(value()).to.equal(true); setValue(true); check(false); expect(value()).to.equal(false); setValue(false); check(true); expect(value()).to.equal(true); }); it('should ignore props.children if children are manually specified', () => { expect(
    c
    ).to.eql(
    c
    ); }); it('should reorder child pairs', () => { let root = render((
    a b
    ), scratch, root); let a = scratch.firstChild.firstChild; let b = scratch.firstChild.lastChild; expect(a).to.have.property('nodeName', 'A'); expect(b).to.have.property('nodeName', 'B'); root = render((
    b a
    ), scratch, root); expect(scratch.firstChild.firstChild).to.have.property('nodeName', 'B'); expect(scratch.firstChild.lastChild).to.have.property('nodeName', 'A'); expect(scratch.firstChild.firstChild).to.equal(b); expect(scratch.firstChild.lastChild).to.equal(a); }); it('should not merge attributes with node created by the DOM', () => { const html = (htmlString) => { const div = document.createElement('div'); div.innerHTML = htmlString; return div.firstChild; }; const DOMElement = html`
    `; const preactElement =
    ; render(preactElement, scratch, DOMElement); expect(scratch).to.have.property('innerHTML', '
    '); }); it('should skip non-preact elements', () => { class Foo extends Component { render() { let alt = this.props.alt || this.state.alt || this.alt; let c = [ foo, { alt?'alt':'bar' } ]; if (alt) c.reverse(); return
    {c}
    ; } } let comp; let root = render( comp = c } />, scratch, root); let c = document.createElement('c'); c.textContent = 'baz'; comp.base.appendChild(c); let b = document.createElement('b'); b.textContent = 'bat'; comp.base.appendChild(b); expect(scratch.firstChild.children, 'append').to.have.length(4); comp.forceUpdate(); expect(scratch.firstChild.children, 'forceUpdate').to.have.length(4); expect(scratch.innerHTML, 'forceUpdate').to.equal(`
    foobarbazbat
    `); comp.alt = true; comp.forceUpdate(); expect(scratch.firstChild.children, 'forceUpdate alt').to.have.length(4); expect(scratch.innerHTML, 'forceUpdate alt').to.equal(`
    altfoobazbat
    `); // Re-rendering from the root is non-destructive if the root was a previous render: comp.alt = false; root = render( comp = c } />, scratch, root); expect(scratch.firstChild.children, 'root re-render').to.have.length(4); expect(scratch.innerHTML, 'root re-render').to.equal(`
    foobarbazbat
    `); comp.alt = true; root = render( comp = c } />, scratch, root); expect(scratch.firstChild.children, 'root re-render 2').to.have.length(4); expect(scratch.innerHTML, 'root re-render 2').to.equal(`
    altfoobazbat
    `); root = render(
    comp = c } />
    , scratch, root); expect(scratch.firstChild.children, 'root re-render changed').to.have.length(3); expect(scratch.innerHTML, 'root re-render changed').to.equal(`
    foobar
    bazbat
    `); }); // Discussion: https://github.com/developit/preact/issues/287 ('HTMLDataListElement' in window ? it : xit)('should allow to pass through as an attribute', () => { render((
    ), scratch); let html = scratch.firstElementChild.firstElementChild.outerHTML; expect(sortAttributes(html)).to.equal(sortAttributes('')); }); it('should not execute append operation when child is at last', (done) => { let input; class TodoList extends Component { constructor(props) { super(props); this.state = { todos: [], text: '' }; this.setText = this.setText.bind(this); this.addTodo = this.addTodo.bind(this); } setText(e) { this.setState({ text: e.target.value }); } addTodo() { let { todos, text } = this.state; todos = todos.concat({ text }); this.setState({ todos, text: '' }); } render() { const {todos, text} = this.state; return (
    { todos.map( todo => (
    {todo.text}
    )) } input = i} />
    ); } } const root = render(, scratch); input.focus(); input.value = 1; root._component.setText({ target: input }); root._component.addTodo(); expect(document.activeElement).to.equal(input); setTimeout(() =>{ expect(/1/.test(scratch.innerHTML)).to.equal(true); done(); }, 10); }); }); preact-8.2.5/test/browser/spec.js000066400000000000000000000107411315110146500167360ustar00rootroot00000000000000import { h, render, rerender, Component } from '../../src/preact'; /** @jsx h */ const EMPTY_CHILDREN = []; describe('Component spec', () => { let scratch; before( () => { scratch = document.createElement('div'); (document.body || document.documentElement).appendChild(scratch); }); beforeEach( () => { scratch.innerHTML = ''; }); after( () => { scratch.parentNode.removeChild(scratch); scratch = null; }); describe('defaultProps', () => { it('should apply default props on initial render', () => { class WithDefaultProps extends Component { constructor(props, context) { super(props, context); expect(props).to.be.deep.equal({ children: EMPTY_CHILDREN, fieldA: 1, fieldB: 2, fieldC: 1, fieldD: 2 }); } render() { return
    ; } } WithDefaultProps.defaultProps = { fieldC: 1, fieldD: 1 }; render(, scratch); }); it('should apply default props on rerender', () => { let doRender; class Outer extends Component { constructor() { super(); this.state = { i:1 }; } componentDidMount() { doRender = () => this.setState({ i: 2 }); } render(props, { i }) { return ; } } class WithDefaultProps extends Component { constructor(props, context) { super(props, context); this.ctor(props, context); } ctor(){} componentWillReceiveProps() {} render() { return
    ; } } WithDefaultProps.defaultProps = { fieldC: 1, fieldD: 1 }; let proto = WithDefaultProps.prototype; sinon.spy(proto, 'ctor'); sinon.spy(proto, 'componentWillReceiveProps'); sinon.spy(proto, 'render'); render(, scratch); doRender(); const PROPS1 = { fieldA: 1, fieldB: 1, fieldC: 1, fieldD: 1 }; const PROPS2 = { fieldA: 1, fieldB: 2, fieldC: 1, fieldD: 2 }; expect(proto.ctor).to.have.been.calledWithMatch(PROPS1); expect(proto.render).to.have.been.calledWithMatch(PROPS1); rerender(); // expect(proto.ctor).to.have.been.calledWith(PROPS2); expect(proto.componentWillReceiveProps).to.have.been.calledWithMatch(PROPS2); expect(proto.render).to.have.been.calledWithMatch(PROPS2); }); // @TODO: migrate this to preact-compat xit('should cache default props', () => { class WithDefaultProps extends Component { constructor(props, context) { super(props, context); expect(props).to.be.deep.equal({ fieldA: 1, fieldB: 2, fieldC: 1, fieldD: 2, fieldX: 10 }); } getDefaultProps() { return { fieldA: 1, fieldB: 1 }; } render() { return
    ; } } WithDefaultProps.defaultProps = { fieldC: 1, fieldD: 1 }; sinon.spy(WithDefaultProps.prototype, 'getDefaultProps'); render((
    ), scratch); expect(WithDefaultProps.prototype.getDefaultProps).to.be.calledOnce; }); }); describe('forceUpdate', () => { it('should force a rerender', () => { let forceUpdate; class ForceUpdateComponent extends Component { componentWillUpdate() {} componentDidMount() { forceUpdate = () => this.forceUpdate(); } render() { return
    ; } } sinon.spy(ForceUpdateComponent.prototype, 'componentWillUpdate'); sinon.spy(ForceUpdateComponent.prototype, 'forceUpdate'); render(, scratch); expect(ForceUpdateComponent.prototype.componentWillUpdate).not.to.have.been.called; forceUpdate(); expect(ForceUpdateComponent.prototype.componentWillUpdate).to.have.been.called; expect(ForceUpdateComponent.prototype.forceUpdate).to.have.been.called; }); it('should add callback to renderCallbacks', () => { let forceUpdate; let callback = sinon.spy(); class ForceUpdateComponent extends Component { componentDidMount() { forceUpdate = () => this.forceUpdate(callback); } render() { return
    ; } } sinon.spy(ForceUpdateComponent.prototype, 'forceUpdate'); render(, scratch); forceUpdate(); expect(ForceUpdateComponent.prototype.forceUpdate).to.have.been.called; expect(ForceUpdateComponent.prototype.forceUpdate).to.have.been.calledWith(callback); expect(callback).to.have.been.called; }); }); }); preact-8.2.5/test/browser/svg.js000066400000000000000000000073121315110146500166030ustar00rootroot00000000000000import { h, render } from '../../src/preact'; /** @jsx h */ // hacky normalization of attribute order across browsers. function sortAttributes(html) { return html.replace(/<([a-z0-9-]+)((?:\s[a-z0-9:_.-]+=".*?")+)((?:\s*\/)?>)/gi, (s, pre, attrs, after) => { let list = attrs.match(/\s[a-z0-9:_.-]+=".*?"/gi).sort( (a, b) => a>b ? 1 : -1 ); if (~after.indexOf('/')) after = '>'; return '<' + pre + list.join('') + after; }); } describe('svg', () => { let scratch; before( () => { scratch = document.createElement('div'); (document.body || document.documentElement).appendChild(scratch); }); beforeEach( () => { scratch.innerHTML = ''; }); after( () => { scratch.parentNode.removeChild(scratch); scratch = null; }); it('should render SVG to string', () => { render(( ), scratch); let html = sortAttributes(String(scratch.innerHTML).replace(' xmlns="http://www.w3.org/2000/svg"', '')); expect(html).to.equal(sortAttributes(` `.replace(/[\n\t]+/g,''))); }); it('should render SVG to DOM', () => { const Demo = () => ( ); render(, scratch); let html = sortAttributes(String(scratch.innerHTML).replace(' xmlns="http://www.w3.org/2000/svg"', '')); expect(html).to.equal(sortAttributes('')); }); it('should render with the correct namespace URI', () => { render(, scratch); let namespace = scratch.querySelector('svg').namespaceURI; expect(namespace).to.equal("http://www.w3.org/2000/svg"); }); it('should use attributes for className', () => { const Demo = ({ c }) => ( ); let root = render(, scratch, root); sinon.spy(root, 'removeAttribute'); root = render(, scratch, root); expect(root.removeAttribute).to.have.been.calledOnce.and.calledWith('class'); root.removeAttribute.restore(); root = render(
    , scratch, root); root = render(, scratch, root); sinon.spy(root, 'setAttribute'); root = render(, scratch, root); expect(root.setAttribute).to.have.been.calledOnce.and.calledWith('class', 'foo_2'); root.setAttribute.restore(); root = render(, scratch, root); root = render(, scratch, root); }); it('should still support class attribute', () => { render(( ), scratch); expect(scratch.innerHTML).to.contain(` class="foo bar"`); }); it('should switch back to HTML for ', () => { render(( test ), scratch); expect(scratch.getElementsByTagName('a')) .to.have.property('0') .that.is.a('HTMLAnchorElement'); }); }); preact-8.2.5/test/karma.conf.js000066400000000000000000000071531315110146500163430ustar00rootroot00000000000000/*eslint no-var:0, object-shorthand:0 */ var coverage = String(process.env.COVERAGE)!=='false', ci = String(process.env.CI).match(/^(1|true)$/gi), pullRequest = !String(process.env.TRAVIS_PULL_REQUEST).match(/^(0|false|undefined)$/gi), masterBranch = String(process.env.TRAVIS_BRANCH).match(/^master$/gi), realBrowser = String(process.env.BROWSER).match(/^(1|true)$/gi), sauceLabs = realBrowser && ci && !pullRequest && masterBranch, performance = !coverage && !realBrowser && String(process.env.PERFORMANCE)!=='false', webpack = require('webpack'); var sauceLabsLaunchers = { sl_chrome: { base: 'SauceLabs', browserName: 'chrome', platform: 'Windows 10' }, sl_firefox: { base: 'SauceLabs', browserName: 'firefox', platform: 'Windows 10' }, sl_safari: { base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.11' }, sl_edge: { base: 'SauceLabs', browserName: 'MicrosoftEdge', platform: 'Windows 10' }, sl_ie_11: { base: 'SauceLabs', browserName: 'internet explorer', version: '11.103', platform: 'Windows 10' }, sl_ie_10: { base: 'SauceLabs', browserName: 'internet explorer', version: '10.0', platform: 'Windows 7' }, sl_ie_9: { base: 'SauceLabs', browserName: 'internet explorer', version: '9.0', platform: 'Windows 7' } }; var travisLaunchers = { chrome_travis: { base: 'Chrome', flags: ['--no-sandbox'] } }; var localBrowsers = realBrowser ? Object.keys(travisLaunchers) : ['PhantomJS']; module.exports = function(config) { config.set({ browsers: sauceLabs ? Object.keys(sauceLabsLaunchers) : localBrowsers, frameworks: ['source-map-support', 'mocha', 'chai-sinon'], reporters: ['mocha'].concat( coverage ? 'coverage' : [], sauceLabs ? 'saucelabs' : [] ), coverageReporter: { dir: __dirname+'/../coverage', reporters: [ { type: 'text-summary' }, { type: 'html' }, { type: 'lcovonly', subdir: '.', file: 'lcov.info' } ] }, mochaReporter: { showDiff: true }, browserLogOptions: { terminal: true }, browserConsoleLogOptions: { terminal: true }, browserNoActivityTimeout: 5 * 60 * 1000, // Use only two browsers concurrently, works better with open source Sauce Labs remote testing concurrency: 2, // sauceLabs: { // tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER || ('local'+require('./package.json').version), // startConnect: false // }, customLaunchers: sauceLabs ? sauceLabsLaunchers : travisLaunchers, files: [ { pattern: 'polyfills.js', watched: false }, { pattern: '{browser,shared}/**.js', watched: false } ], preprocessors: { '**/*': ['webpack', 'sourcemap'] }, webpack: { devtool: 'inline-source-map', module: { /* Transpile source and test files */ rules: [ { enforce: 'pre', test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader', options: { comments: false, compact: true } }, /* Only Instrument our source files for coverage */ coverage ? { test: /\.jsx?$/, loader: 'isparta-loader', include: /src/ } : {} ] }, resolve: { // The React DevTools integration requires preact as a module // rather than referencing source files inside the module // directly alias: { preact: '../src/preact' }, modules: [__dirname, 'node_modules'] }, plugins: [ new webpack.DefinePlugin({ coverage: coverage, NODE_ENV: JSON.stringify(process.env.NODE_ENV || ''), ENABLE_PERFORMANCE: performance, DISABLE_FLAKEY: !!String(process.env.FLAKEY).match(/^(0|false)$/gi) }) ] }, webpackMiddleware: { noInfo: true } }); }; preact-8.2.5/test/node/000077500000000000000000000000001315110146500147055ustar00rootroot00000000000000preact-8.2.5/test/node/index.js000066400000000000000000000000361315110146500163510ustar00rootroot00000000000000// this is just a placeholder preact-8.2.5/test/polyfills.js000066400000000000000000000002601315110146500163310ustar00rootroot00000000000000// ES2015 APIs used by developer tools integration import 'core-js/es6/map'; import 'core-js/fn/array/fill'; import 'core-js/fn/array/from'; import 'core-js/fn/object/assign'; preact-8.2.5/test/shared/000077500000000000000000000000001315110146500152265ustar00rootroot00000000000000preact-8.2.5/test/shared/exports.js000066400000000000000000000014011315110146500172640ustar00rootroot00000000000000import preact, { h, Component, render, rerender, options } from '../../src/preact'; import { expect } from 'chai'; describe('preact', () => { it('should be available as a default export', () => { expect(preact).to.be.an('object'); expect(preact).to.have.property('h', h); expect(preact).to.have.property('Component', Component); expect(preact).to.have.property('render', render); expect(preact).to.have.property('rerender', rerender); expect(preact).to.have.property('options', options); }); it('should be available as named exports', () => { expect(h).to.be.a('function'); expect(Component).to.be.a('function'); expect(render).to.be.a('function'); expect(rerender).to.be.a('function'); expect(options).to.exist.and.be.an('object'); }); }); preact-8.2.5/test/shared/h.js000066400000000000000000000105621315110146500160170ustar00rootroot00000000000000import { h } from '../../src/preact'; import { VNode } from '../../src/vnode'; import { expect } from 'chai'; /*eslint-env browser, mocha */ /** @jsx h */ const buildVNode = (nodeName, attributes, children=[]) => ({ nodeName, children, attributes, key: attributes && attributes.key }); describe('h(jsx)', () => { it('should return a VNode', () => { let r; expect( () => r = h('foo') ).not.to.throw(); expect(r).to.be.an('object'); expect(r).to.be.an.instanceof(VNode); expect(r).to.have.property('nodeName', 'foo'); expect(r).to.have.property('attributes', undefined); expect(r).to.have.property('children').that.eql([]); }); it('should perserve raw attributes', () => { let attrs = { foo:'bar', baz:10, func:()=>{} }, r = h('foo', attrs); expect(r).to.be.an('object') .with.property('attributes') .that.deep.equals(attrs); }); it('should support element children', () => { let r = h( 'foo', null, h('bar'), h('baz') ); expect(r).to.be.an('object') .with.property('children') .that.deep.equals([ buildVNode('bar'), buildVNode('baz') ]); }); it('should support multiple element children, given as arg list', () => { let r = h( 'foo', null, h('bar'), h('baz', null, h('test')) ); expect(r).to.be.an('object') .with.property('children') .that.deep.equals([ buildVNode('bar'), buildVNode('baz', undefined, [ buildVNode('test') ]) ]); }); it('should handle multiple element children, given as an array', () => { let r = h( 'foo', null, [ h('bar'), h('baz', null, h('test')) ] ); expect(r).to.be.an('object') .with.property('children') .that.deep.equals([ buildVNode('bar'), buildVNode('baz', undefined, [ buildVNode('test') ]) ]); }); it('should handle multiple children, flattening one layer as needed', () => { let r = h( 'foo', null, h('bar'), [ h('baz', null, h('test')) ] ); expect(r).to.be.an('object') .with.property('children') .that.deep.equals([ buildVNode('bar'), buildVNode('baz', undefined, [ buildVNode('test') ]) ]); }); it('should support nested children', () => { const m = x => h(x); expect( h('foo', null, m('a'), [m('b'), m('c')], m('d')) ).to.have.property('children').that.eql(['a', 'b', 'c', 'd'].map(m)); expect( h('foo', null, [m('a'), [m('b'), m('c')], m('d')]) ).to.have.property('children').that.eql(['a', 'b', 'c', 'd'].map(m)); expect( h('foo', { children: [m('a'), [m('b'), m('c')], m('d')] }) ).to.have.property('children').that.eql(['a', 'b', 'c', 'd'].map(m)); expect( h('foo', { children: [[m('a'), [m('b'), m('c')], m('d')]] }) ).to.have.property('children').that.eql(['a', 'b', 'c', 'd'].map(m)); expect( h('foo', { children: m('a') }) ).to.have.property('children').that.eql([m('a')]); expect( h('foo', { children: 'a' }) ).to.have.property('children').that.eql(['a']); }); it('should support text children', () => { let r = h( 'foo', null, 'textstuff' ); expect(r).to.be.an('object') .with.property('children') .with.length(1) .with.property('0') .that.equals('textstuff'); }); it('should merge adjacent text children', () => { let r = h( 'foo', null, 'one', 'two', h('bar'), 'three', h('baz'), h('baz'), 'four', null, 'five', 'six' ); expect(r).to.be.an('object') .with.property('children') .that.deep.equals([ 'onetwo', buildVNode('bar'), 'three', buildVNode('baz'), buildVNode('baz'), 'fourfivesix' ]); }); it('should merge nested adjacent text children', () => { let r = h( 'foo', null, 'one', ['two', null, 'three'], null, ['four', null, 'five', null], 'six', null ); expect(r).to.be.an('object') .with.property('children') .that.deep.equals([ 'onetwothreefourfivesix' ]); }); it('should not merge children that are boolean values', () => { let r = h( 'foo', null, 'one', true, 'two', false, 'three' ); expect(r).to.be.an('object') .with.property('children') .that.deep.equals(['onetwothree']); }); it('should not merge children of components', () => { let Component = ({children}) => children; let r = h(Component, null, 'x', 'y'); expect(r).to.be.an('object') .with.property('children') .that.deep.equals(['x', 'y']); }); }); preact-8.2.5/test/ts/000077500000000000000000000000001315110146500144065ustar00rootroot00000000000000preact-8.2.5/test/ts/prect-test.tsx000066400000000000000000000013471315110146500172450ustar00rootroot00000000000000import { h, render, Component } from 'preact'; interface DummyProps { initialInput: string; } interface DummyState { input: string; } class DummyComponent extends Component { constructor(props: DummyProps) { super(props); this.state = { input: `x${this.props}x` } } render({ initialInput }: DummyProps, { input }: DummyState) { return } } interface DummerComponentProps extends DummyProps, DummyState { } function DummerComponent({ input, initialInput }: DummerComponentProps) { return
    Input: {input}, initial: {initialInput}
    ; } render(h(DummerComponent, { initialInput: "The input" }), document.getElementById("xxx")); preact-8.2.5/test/ts/tsconfig.json000066400000000000000000000005731315110146500171220ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "lib": [ "es6", "dom" ], "noImplicitAny": true, "noImplicitThis": true, "strictNullChecks": false, "typeRoots": [ "../../" ], "types": [], "noEmit": true, "forceConsistentCasingInFileNames": true, "jsx": "react", "jsxFactory": "h" }, "files": [ "prect-test.tsx", "../../src/preact.d.ts" ] } preact-8.2.5/typings.json000066400000000000000000000001101315110146500153610ustar00rootroot00000000000000{ "name": "preact", "main": "src/preact.d.ts", "version": false }