Repository: crcn/paperclip Branch: master Commit: 26fd98144e56 Files: 1132 Total size: 9.1 MB Directory structure: gitextract__bqhh5jc/ ├── .github/ │ └── workflows/ │ ├── checks.yml │ ├── documentation.yml │ └── publish.yml ├── .gitignore ├── .husky/ │ └── pre-commit ├── .prettierignore ├── .vscode/ │ └── launch.json ├── .yarn/ │ ├── plugins/ │ │ └── @yarnpkg/ │ │ └── plugin-workspace-tools.cjs │ └── releases/ │ └── yarn-1.22.17.cjs ├── .yarnarc ├── .yarnrc.yml ├── LICENSE ├── README.md ├── archive/ │ ├── figma-paperclip/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── bin/ │ │ │ └── figma-paperclip │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── api.ts │ │ │ ├── cli.ts │ │ │ ├── constants.ts │ │ │ ├── index.ts │ │ │ ├── memo.ts │ │ │ ├── state.ts │ │ │ ├── translate-pc.ts │ │ │ ├── translate-utils.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── gatsby-plugin-paperclip/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── gatsby-node.ts │ │ │ ├── index.ts │ │ │ └── resolve.ts │ │ └── tsconfig.json │ ├── paperclip-docco/ │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── brainstorm/ │ │ │ └── ui/ │ │ │ ├── app.pc │ │ │ ├── colors.pc │ │ │ ├── test.html │ │ │ └── typography.pc │ │ ├── package.json │ │ ├── paperclip.config.json │ │ └── src/ │ │ └── front-end/ │ │ └── index.ts │ └── paperclip-playground/ │ ├── package.json │ ├── paperclip.config.json │ ├── src/ │ │ ├── frontend/ │ │ │ ├── actions/ │ │ │ │ ├── base.ts │ │ │ │ └── index.ts │ │ │ ├── api/ │ │ │ │ └── index.ts │ │ │ ├── components/ │ │ │ │ ├── Button/ │ │ │ │ │ ├── index.pc │ │ │ │ │ └── index.tsx │ │ │ │ ├── Main/ │ │ │ │ │ ├── CodeMode/ │ │ │ │ │ │ ├── Slim.tsx │ │ │ │ │ │ ├── Toolbar/ │ │ │ │ │ │ │ └── index.pc │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── prism.css │ │ │ │ │ ├── DesignMode/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── EditableLabel/ │ │ │ │ │ │ └── index.pc │ │ │ │ │ ├── Menu/ │ │ │ │ │ │ └── index.pc │ │ │ │ │ ├── NoBrowserSupportModal/ │ │ │ │ │ │ └── index.pc │ │ │ │ │ ├── PasswordModal/ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ProjectLoadingModal/ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── Projects/ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ShareModal/ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── Toolbar/ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── auth/ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── oauth.ts │ │ │ │ │ ├── index.pc │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── test.pc │ │ │ │ ├── Modal/ │ │ │ │ │ ├── index.pc │ │ │ │ │ └── index.tsx │ │ │ │ └── Theme/ │ │ │ │ ├── breakpoints.pc │ │ │ │ └── index.pc │ │ │ ├── entry.tsx │ │ │ ├── hocs/ │ │ │ │ └── withAppStore/ │ │ │ │ └── index.tsx │ │ │ ├── hooks/ │ │ │ │ ├── resources.ts │ │ │ │ ├── useAppStore/ │ │ │ │ │ └── index.ts │ │ │ │ └── useQuery.ts │ │ │ ├── reducers/ │ │ │ │ └── index.ts │ │ │ ├── sagas/ │ │ │ │ ├── api.ts │ │ │ │ ├── engine-worker.ts │ │ │ │ ├── engine.ts │ │ │ │ ├── index.ts │ │ │ │ ├── location.ts │ │ │ │ ├── resources.ts │ │ │ │ └── utils.ts │ │ │ ├── state/ │ │ │ │ └── index.ts │ │ │ └── utils/ │ │ │ └── string-editor.ts │ │ ├── index.html │ │ └── index.ts │ ├── todos.md │ ├── tsconfig.json │ └── webpack.config.js ├── examples/ │ ├── react-basic/ │ │ ├── README.md │ │ ├── package.json │ │ ├── paperclip.config.json │ │ ├── sandbox.config.json │ │ ├── src/ │ │ │ ├── CustomGroceryList.pc │ │ │ ├── GroceryList.pc │ │ │ ├── GroceryList.tsx │ │ │ ├── breakpoints.pc │ │ │ ├── entry.tsx │ │ │ └── index.html │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── syntax-basic/ │ │ ├── README.md │ │ ├── package.json │ │ ├── paperclip.config.json │ │ ├── sandbox.config.json │ │ └── src/ │ │ └── hello.pc │ ├── tailwind/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── paperclip.config.json │ │ ├── sandbox.config.json │ │ ├── src/ │ │ │ ├── index.pc │ │ │ └── tailwind.scss │ │ └── tailwind.config.js │ ├── tailwind-and-animate/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── css-modules/ │ │ │ └── animate.css │ │ ├── package.json │ │ ├── paperclip.config.json │ │ ├── sandbox.config.json │ │ ├── src/ │ │ │ ├── hello-paperclip.pc │ │ │ └── tailwind.scss │ │ └── tailwind.config.js │ └── tailwind-and-bootstrap-example/ │ ├── .gitignore │ ├── css-modules/ │ │ └── bootstrap/ │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.rtl.css │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.rtl.css │ │ ├── bootstrap-utilities.css │ │ ├── bootstrap-utilities.rtl.css │ │ ├── bootstrap.css │ │ └── bootstrap.rtl.css │ ├── package.json │ ├── paperclip.config.json │ ├── src/ │ │ ├── hello-paperclip.pc │ │ └── tailwind.scss │ └── tailwind.config.js ├── lerna.json ├── package.json ├── packages/ │ ├── avocode-paperclip/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── package.json │ │ ├── src/ │ │ │ ├── api.ts │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── figma-paperclip/ │ │ ├── LICENSE │ │ ├── bin/ │ │ │ └── figma-paperclip │ │ ├── examples/ │ │ │ └── wrapper-module/ │ │ │ ├── .gitignore │ │ │ ├── figma-paperclip.config.js │ │ │ ├── figma-paperclip.json.backup │ │ │ ├── package.json │ │ │ └── paperclip.config.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── api.ts │ │ │ ├── constants.ts │ │ │ ├── graph.ts │ │ │ ├── index.ts │ │ │ ├── init.ts │ │ │ ├── memo.ts │ │ │ ├── pull.ts │ │ │ ├── state.ts │ │ │ ├── translate/ │ │ │ │ ├── context.ts │ │ │ │ ├── index.ts │ │ │ │ ├── modules.ts │ │ │ │ ├── pages.ts │ │ │ │ └── utils.ts │ │ │ ├── translate2/ │ │ │ │ ├── context.ts │ │ │ │ ├── index.ts │ │ │ │ ├── pages.ts │ │ │ │ └── state.ts │ │ │ └── utils.ts │ │ ├── todos.md │ │ └── tsconfig.json │ ├── jest-paperclip/ │ │ ├── LICENSE │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── paperclip-autocomplete/ │ │ ├── .eslintrc.json │ │ ├── LICENSE │ │ ├── README.md │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── string-scanner.ts │ │ │ ├── suggest-context.ts │ │ │ ├── test/ │ │ │ │ ├── suggest-context-test.ts │ │ │ │ └── tokenize-test.ts │ │ │ └── tokenizer.ts │ │ └── tsconfig.json │ ├── paperclip-builder/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── build.ts │ │ │ ├── errors.ts │ │ │ ├── index.ts │ │ │ ├── resolve-compilers.ts │ │ │ ├── test/ │ │ │ │ ├── basic-test.ts │ │ │ │ ├── build-test.ts │ │ │ │ ├── resolve-compilers-test.ts │ │ │ │ └── utils.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── paperclip-cli/ │ │ ├── .eslintrc.json │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bin/ │ │ │ └── paperclip │ │ ├── package.json │ │ ├── src/ │ │ │ ├── cli.ts │ │ │ ├── coverage.ts │ │ │ ├── dev.ts │ │ │ ├── init.ts │ │ │ ├── install-module.ts │ │ │ ├── pretty-message.ts │ │ │ ├── scaffolding/ │ │ │ │ ├── generators/ │ │ │ │ │ ├── base.ts │ │ │ │ │ ├── files/ │ │ │ │ │ │ ├── .babelrc │ │ │ │ │ │ ├── entry-tsx │ │ │ │ │ │ ├── hello-paperclip.pc │ │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ │ └── webpack.config-js │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── javascript.ts │ │ │ │ │ ├── node.ts │ │ │ │ │ ├── percy.ts │ │ │ │ │ ├── react.ts │ │ │ │ │ ├── root.ts │ │ │ │ │ ├── typescript.ts │ │ │ │ │ ├── utils.ts │ │ │ │ │ └── webpack.ts │ │ │ │ └── index.ts │ │ │ └── test/ │ │ │ └── basic-test.ts │ │ └── tsconfig.json │ ├── paperclip-cli-minimal/ │ │ ├── .eslintrc.json │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bin/ │ │ │ └── paperclip │ │ ├── package.json │ │ ├── src/ │ │ │ ├── build.ts │ │ │ ├── cli.ts │ │ │ └── tm.d.ts │ │ └── tsconfig.json │ ├── paperclip-cli-utils/ │ │ ├── .eslintrc.json │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bin/ │ │ │ └── paperclip │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── pretty-message.ts │ │ └── tsconfig.json │ ├── paperclip-common/ │ │ ├── LICENSE │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── bindable/ │ │ │ │ └── index.ts │ │ │ ├── disposable.ts │ │ │ ├── em.ts │ │ │ ├── events.ts │ │ │ ├── immutable-store.ts │ │ │ ├── index.ts │ │ │ ├── kernel.ts │ │ │ ├── log.ts │ │ │ ├── promise.ts │ │ │ ├── remote-channel.ts │ │ │ ├── rpc/ │ │ │ │ ├── adapters.ts │ │ │ │ ├── chan.ts │ │ │ │ ├── index.ts │ │ │ │ └── spy.ts │ │ │ ├── services.ts │ │ │ ├── string-editor.ts │ │ │ └── test-utils/ │ │ │ ├── index.ts │ │ │ └── tmp-fixtures.ts │ │ └── tsconfig.json │ ├── paperclip-compiler-base-jsx/ │ │ ├── .eslintrc.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── code-compiler.ts │ │ │ ├── definition-compiler.ts │ │ │ ├── index.ts │ │ │ ├── test/ │ │ │ │ ├── index.ts │ │ │ │ ├── test-suite.ts │ │ │ │ └── utils.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── paperclip-compiler-html/ │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── test/ │ │ │ │ └── basic-test.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── paperclip-compiler-php/ │ │ └── examples/ │ │ └── test/ │ │ └── src/ │ │ └── test.pc.php │ ├── paperclip-compiler-react/ │ │ ├── .eslintrc.json │ │ ├── LICENSE │ │ ├── README.md │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── inernals.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── test/ │ │ │ ├── basic-test.tsx │ │ │ └── css-test.tsx │ │ └── tsconfig.json │ ├── paperclip-core/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── babel.config.json │ │ ├── browser.d.ts │ │ ├── browser.js │ │ ├── examples/ │ │ │ ├── codepen/ │ │ │ │ ├── bird-game.pc │ │ │ │ ├── pure-css-accordion.pc │ │ │ │ └── pure-css-buttons.pc │ │ │ ├── paperclip.config.json │ │ │ └── test.css │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── native/ │ │ │ ├── Cargo.toml │ │ │ ├── artifacts.json │ │ │ ├── build.rsx │ │ │ ├── bundler/ │ │ │ │ ├── paperclip.d.ts │ │ │ │ ├── paperclip.js │ │ │ │ ├── paperclip_bg.js │ │ │ │ ├── paperclip_bg.wasm │ │ │ │ └── paperclip_bg.wasm.d.ts │ │ │ ├── rustfmt.toml │ │ │ └── src/ │ │ │ ├── annotation/ │ │ │ │ ├── ast.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── parser.rs │ │ │ │ ├── runtime.rs │ │ │ │ └── tokenizer.rs │ │ │ ├── base/ │ │ │ │ ├── ast.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── parser.rs │ │ │ │ ├── runtime.rs │ │ │ │ ├── string_scanner.rs │ │ │ │ ├── tokenizer.rs │ │ │ │ └── utils.rs │ │ │ ├── core/ │ │ │ │ ├── ast.rs │ │ │ │ ├── diagnostics.rs │ │ │ │ ├── eval.rs │ │ │ │ ├── eval_utils.rs │ │ │ │ ├── graph.rs │ │ │ │ ├── id_generator.rs │ │ │ │ ├── mod.rs │ │ │ │ └── vfs.rs │ │ │ ├── coverage/ │ │ │ │ ├── mod.rs │ │ │ │ └── reporter.rs │ │ │ ├── css/ │ │ │ │ ├── ast.rs │ │ │ │ ├── base.rs │ │ │ │ ├── declaration_value_ast.rs │ │ │ │ ├── declaration_value_parser.rs │ │ │ │ ├── media_ast.rs │ │ │ │ ├── media_parser.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── parser.rs │ │ │ │ ├── runtime/ │ │ │ │ │ ├── cache.rs │ │ │ │ │ ├── diff.rs │ │ │ │ │ ├── evaluator.rs │ │ │ │ │ ├── export.rs │ │ │ │ │ ├── media_match.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── mutation.rs │ │ │ │ │ ├── specificity.rs │ │ │ │ │ └── virt.rs │ │ │ │ └── tokenizer.rs │ │ │ ├── engine/ │ │ │ │ ├── diagnostics.rs │ │ │ │ ├── engine.rs │ │ │ │ ├── errors.rs │ │ │ │ ├── mod.rs │ │ │ │ └── test_utils.rs │ │ │ ├── lib.rs │ │ │ ├── pc/ │ │ │ │ ├── ast.rs │ │ │ │ ├── get_info.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── parser.rs │ │ │ │ ├── runtime/ │ │ │ │ │ ├── annotation_evaluator.rs │ │ │ │ │ ├── cache.rs │ │ │ │ │ ├── diff.rs │ │ │ │ │ ├── evaluator.rs │ │ │ │ │ ├── export.rs │ │ │ │ │ ├── inspect_node_styles.rs │ │ │ │ │ ├── inspect_selector_info.rs │ │ │ │ │ ├── inspector.rs │ │ │ │ │ ├── lint.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── mutation.rs │ │ │ │ │ ├── selector_match.rs │ │ │ │ │ └── virt.rs │ │ │ │ └── tokenizer.rs │ │ │ └── script/ │ │ │ ├── ast.rs │ │ │ ├── mod.rs │ │ │ ├── parser.rs │ │ │ ├── runtime/ │ │ │ │ ├── evaluator.rs │ │ │ │ ├── mod.rs │ │ │ │ └── virt.rs │ │ │ └── tokenizer.rs │ │ ├── package.json │ │ ├── pkg/ │ │ │ └── index.js │ │ ├── src/ │ │ │ ├── core/ │ │ │ │ ├── delegate.ts │ │ │ │ ├── index.ts │ │ │ │ ├── infer.ts │ │ │ │ └── utils.ts │ │ │ ├── node/ │ │ │ │ ├── async-delegate.ts │ │ │ │ ├── delegate-worker.ts │ │ │ │ ├── index.ts │ │ │ │ └── sync-delegate.ts │ │ │ └── test/ │ │ │ ├── file-system/ │ │ │ │ ├── pc-test.ts │ │ │ │ └── resolve-test.ts │ │ │ ├── utils.ts │ │ │ └── virtual/ │ │ │ ├── annotate-test.ts │ │ │ ├── css-compile-test.ts │ │ │ ├── css-test.ts │ │ │ ├── error-test.ts │ │ │ ├── infer-test.ts │ │ │ ├── inspect-test.ts │ │ │ ├── lint-test.ts │ │ │ ├── pc-test.ts │ │ │ └── raws-test.ts │ │ ├── test-fixtures/ │ │ │ ├── .vscode/ │ │ │ │ └── settings.json │ │ │ ├── modules/ │ │ │ │ ├── @nested/ │ │ │ │ │ └── in/ │ │ │ │ │ └── a/ │ │ │ │ │ └── folder/ │ │ │ │ │ ├── package.json │ │ │ │ │ ├── paperclip.config.json │ │ │ │ │ └── src/ │ │ │ │ │ ├── component.pc │ │ │ │ │ ├── imp-mod-a.pc │ │ │ │ │ ├── module.pc │ │ │ │ │ └── test.pc │ │ │ │ └── module-a/ │ │ │ │ ├── package.json │ │ │ │ ├── paperclip.config.json │ │ │ │ └── src/ │ │ │ │ ├── component.pc │ │ │ │ ├── module.pc │ │ │ │ └── test.pc │ │ │ ├── paperclip.config.json │ │ │ └── src/ │ │ │ ├── bad-css-url.pc │ │ │ ├── bad-import.pc │ │ │ ├── good-import.pc │ │ │ ├── hello-world.pc │ │ │ ├── mod-a-import.pc │ │ │ ├── mod-import.pc │ │ │ ├── module.pc │ │ │ └── nested-mod-import.pc │ │ ├── tsconfig.esm.json │ │ └── tsconfig.json │ ├── paperclip-coverage/ │ │ ├── package.json │ │ ├── paperclip.config.json │ │ ├── src/ │ │ │ ├── constants.ts │ │ │ ├── generate/ │ │ │ │ ├── html/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── atoms.pc │ │ │ │ │ ├── file-report.pc │ │ │ │ │ ├── page.pc │ │ │ │ │ └── report.pc │ │ │ │ └── stdout/ │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── state.ts │ │ └── tsconfig.json │ ├── paperclip-diff/ │ │ ├── package.json │ │ ├── paperclip.config.json │ │ ├── src/ │ │ │ ├── cli.ts │ │ │ ├── core.ts │ │ │ ├── index.ts │ │ │ ├── report/ │ │ │ │ ├── html/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── page.pc │ │ │ │ │ └── report.pc │ │ │ │ ├── index.ts │ │ │ │ └── stdout/ │ │ │ │ └── index.ts │ │ │ ├── snapshots.ts │ │ │ ├── state.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── paperclip-diff-utils/ │ │ ├── bin/ │ │ │ └── paperclip-diff │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── pc-document.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── paperclip-editor-engine/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── client/ │ │ │ │ ├── client.ts │ │ │ │ ├── documents/ │ │ │ │ │ ├── base.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── pc/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── source.ts │ │ │ │ └── internal-client.ts │ │ │ ├── core/ │ │ │ │ ├── channels/ │ │ │ │ │ ├── document.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── virtual-object.ts │ │ │ │ ├── crdt-document.ts │ │ │ │ ├── documents.ts │ │ │ │ ├── index.ts │ │ │ │ ├── string-editor.ts │ │ │ │ ├── utils.ts │ │ │ │ └── virtual-object-edit.ts │ │ │ ├── host/ │ │ │ │ ├── connection.ts │ │ │ │ ├── documents/ │ │ │ │ │ ├── base.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── manager.ts │ │ │ │ │ ├── pc-document-editor.ts │ │ │ │ │ ├── pc-source.ts │ │ │ │ │ └── pc.ts │ │ │ │ └── host.ts │ │ │ ├── index.ts │ │ │ └── test/ │ │ │ ├── basic-test.ts │ │ │ ├── preview-test.ts │ │ │ ├── utils.ts │ │ │ └── virtual-edit-test.ts │ │ └── tsconfig.json │ ├── paperclip-interim/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── state/ │ │ │ │ ├── assets.ts │ │ │ │ ├── css.ts │ │ │ │ ├── html.ts │ │ │ │ ├── index.ts │ │ │ │ ├── module.ts │ │ │ │ ├── options.ts │ │ │ │ └── script.ts │ │ │ ├── test/ │ │ │ │ ├── basic-test.ts │ │ │ │ └── utils.ts │ │ │ └── translate/ │ │ │ ├── assets.ts │ │ │ ├── css.ts │ │ │ ├── html.ts │ │ │ ├── index.ts │ │ │ ├── module.ts │ │ │ ├── options.ts │ │ │ ├── script.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── paperclip-language-service/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── autocomplete.ts │ │ │ ├── collect-ast-info.ts │ │ │ ├── completion-items.ts │ │ │ ├── css-color-names.ts │ │ │ ├── css-decl-name-constants.ts │ │ │ ├── css-decl-value-constants.ts │ │ │ ├── error-service.ts │ │ │ ├── index.ts │ │ │ ├── language-service.ts │ │ │ ├── state.ts │ │ │ ├── tag-name-constants.ts │ │ │ ├── test/ │ │ │ │ ├── basic-test.ts │ │ │ │ ├── colors-test.ts │ │ │ │ ├── definitions-test.ts │ │ │ │ ├── links-test.ts │ │ │ │ └── suggestion-test.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── paperclip-loader/ │ │ ├── .eslintrc.json │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── test/ │ │ │ └── basic-test.ts │ │ └── tsconfig.json │ ├── paperclip-monaco/ │ │ ├── LICENSE │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── languages/ │ │ │ ├── adapter.ts │ │ │ ├── html.ts │ │ │ ├── pcss.ts │ │ │ ├── register.ts │ │ │ ├── script.ts │ │ │ ├── service/ │ │ │ │ ├── ast-info.ts │ │ │ │ ├── async-provider.ts │ │ │ │ ├── autocomplete.ts │ │ │ │ ├── base.ts │ │ │ │ ├── channel.ts │ │ │ │ ├── css-color-names.ts │ │ │ │ ├── css-constants.ts │ │ │ │ ├── css-declaration-constants.ts │ │ │ │ ├── index.ts │ │ │ │ └── worker.ts │ │ │ └── worker.ts │ │ └── tsconfig.json │ ├── paperclip-repl/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── paperclip.config.json │ │ ├── src/ │ │ │ ├── app.ts │ │ │ ├── controllers/ │ │ │ │ ├── channel-handler.ts │ │ │ │ ├── channels.ts │ │ │ │ ├── designer/ │ │ │ │ │ └── index.ts │ │ │ │ ├── paperclip.ts │ │ │ │ ├── parent.ts │ │ │ │ ├── worker/ │ │ │ │ │ ├── designer-channel-handler.ts │ │ │ │ │ ├── entry.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── io.ts │ │ │ │ │ └── paperclip.ts │ │ │ │ └── worker-connection.ts │ │ │ ├── entry.tsx │ │ │ ├── index.html │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack/ │ │ ├── webpack.config-base.js │ │ ├── webpack.config-esm.js │ │ ├── webpack.config-web.js │ │ └── webpack.config.js │ ├── paperclip-source-writer/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── index.d.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ ├── string-editor.ts │ │ │ ├── test/ │ │ │ │ ├── basic-test.ts │ │ │ │ └── utils.ts │ │ │ └── writer.ts │ │ └── tsconfig.json │ ├── paperclip-test-utils/ │ │ ├── LICENSE │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── paperclip-utils/ │ │ ├── .eslintrc.json │ │ ├── LICENSE │ │ ├── README.md │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── base/ │ │ │ │ ├── ast.ts │ │ │ │ └── virt.ts │ │ │ ├── core/ │ │ │ │ ├── actions.ts │ │ │ │ ├── ast.ts │ │ │ │ ├── config.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── diagnostics.ts │ │ │ │ ├── errors.ts │ │ │ │ ├── events.ts │ │ │ │ ├── graph.ts │ │ │ │ ├── memo.ts │ │ │ │ ├── module-ast.ts │ │ │ │ ├── promise.ts │ │ │ │ ├── report.ts │ │ │ │ ├── resolve.ts │ │ │ │ ├── url.ts │ │ │ │ └── utils.ts │ │ │ ├── css/ │ │ │ │ ├── ast.ts │ │ │ │ ├── decl-value-ast.ts │ │ │ │ ├── exports.ts │ │ │ │ ├── patcher.ts │ │ │ │ ├── stringify-sheet.ts │ │ │ │ ├── style-inspection.ts │ │ │ │ ├── virt-mutation.ts │ │ │ │ └── virt.ts │ │ │ ├── html/ │ │ │ │ ├── ast.ts │ │ │ │ ├── exports.ts │ │ │ │ ├── patcher.ts │ │ │ │ ├── stringify-virt-node.ts │ │ │ │ ├── tree.ts │ │ │ │ ├── virt-mtuation.ts │ │ │ │ └── virt.ts │ │ │ ├── index.ts │ │ │ ├── node/ │ │ │ │ └── source-watcher.ts │ │ │ ├── script/ │ │ │ │ ├── ast.ts │ │ │ │ └── virt.ts │ │ │ └── test/ │ │ │ ├── basic-test.ts │ │ │ └── config-test.ts │ │ └── tsconfig.json │ ├── paperclip-vscode/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── .nycrc.json │ │ ├── .vscodeignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── languages/ │ │ │ ├── paperclip-css-language-configuration.json │ │ │ └── paperclip-html-language-configuration.json │ │ ├── package.json │ │ ├── paperclip/ │ │ │ ├── .vscodeignore │ │ │ ├── CHANGELOG.md │ │ │ ├── README.md │ │ │ ├── language-configuration.json │ │ │ ├── package.json │ │ │ └── syntaxes/ │ │ │ └── paperclip.tmLanguage.json │ │ ├── scripts/ │ │ │ ├── scrape-css-declarations.js │ │ │ └── utils.js │ │ ├── src/ │ │ │ ├── extension/ │ │ │ │ ├── channels.ts │ │ │ │ ├── command-manager.ts │ │ │ │ ├── document-manager.ts │ │ │ │ ├── index.ts │ │ │ │ ├── language/ │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── server/ │ │ │ │ │ ├── connection.ts │ │ │ │ │ ├── design-server.ts │ │ │ │ │ ├── documents.ts │ │ │ │ │ ├── events.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── resolver.ts │ │ │ │ ├── preview/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── live-window-manager.ts │ │ │ │ │ ├── live-window.ts │ │ │ │ │ └── service.ts │ │ │ │ ├── rpc/ │ │ │ │ │ └── index.ts │ │ │ │ ├── utils.ts │ │ │ │ └── version-check.ts │ │ │ └── scripts/ │ │ │ ├── build.ts │ │ │ └── theme.ts │ │ ├── syntaxes/ │ │ │ ├── highlight-test.pc │ │ │ ├── paperclip-css.json │ │ │ ├── paperclip-css.yml │ │ │ ├── paperclip-html.json │ │ │ ├── paperclip-html.yaml │ │ │ └── paperclip.tmLanguage.json │ │ └── tsconfig.json │ ├── paperclip-web-renderer/ │ │ ├── .eslintrc.json │ │ ├── .yo-rc.json │ │ ├── LICENSE │ │ ├── README.md │ │ ├── browser.js │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── base.ts │ │ │ ├── cssom-patcher.ts │ │ │ ├── dom-patcher.ts │ │ │ ├── frame-renderer.ts │ │ │ ├── frame2-renderer.ts │ │ │ ├── frames-patcher.ts │ │ │ ├── index.ts │ │ │ ├── native-renderer.ts │ │ │ ├── renderer.ts │ │ │ ├── test/ │ │ │ │ ├── change-test.ts │ │ │ │ ├── frame-test.ts │ │ │ │ ├── fuzzy-test.ts │ │ │ │ ├── random/ │ │ │ │ │ ├── document.ts │ │ │ │ │ ├── html.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── styles.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── utils.ts │ │ │ └── utils/ │ │ │ ├── index.ts │ │ │ └── misc.ts │ │ ├── tsconfig.json │ │ ├── uis/ │ │ │ ├── loader.pc │ │ │ └── paperclip.config.json │ │ └── webpack.config.js │ ├── paperclip-website/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── babel.config.js │ │ ├── blog/ │ │ │ └── 2020-03-01-introducing-paperclip.md │ │ ├── docs/ │ │ │ ├── configure-jest.md │ │ │ ├── configure-paperclip.md │ │ │ ├── configure-percy.md │ │ │ ├── configure-prettier.md │ │ │ ├── configure-webpack.md │ │ │ ├── getting-started-cofigure.md │ │ │ ├── getting-started-cra.md │ │ │ ├── getting-started-first-ui.md │ │ │ ├── getting-started-new-project.md │ │ │ ├── getting-started-vscode.md │ │ │ ├── getting-started-webpack.md │ │ │ ├── guide-compiler.md │ │ │ ├── guide-dynamic-styles.md │ │ │ ├── guide-how-to-use.md │ │ │ ├── guide-migrating-to-paperclip.md │ │ │ ├── guide-modules.md │ │ │ ├── guide-organization.md │ │ │ ├── guide-theming.md │ │ │ ├── guide-theming2.md │ │ │ ├── guide-thinking-in-paperclip.md │ │ │ ├── guide-third-party-libraries.md │ │ │ ├── guide-visual-tools.md │ │ │ ├── guide-vscode.md │ │ │ ├── guide-why.md │ │ │ ├── guide-workarounds.md │ │ │ ├── guide-writing-components.md │ │ │ ├── guide-writing-previews.md │ │ │ ├── installation.md │ │ │ ├── introduction.md │ │ │ ├── usage-cli.md │ │ │ ├── usage-react.md │ │ │ ├── usage-syntax.md │ │ │ ├── usage-troubleshooting.md │ │ │ ├── visual-regression-tooling.md │ │ │ └── visual-tooling.md │ │ ├── docusaurus.config.js │ │ ├── draft/ │ │ │ ├── 2021-01-05-scoped-css.md │ │ │ ├── 2021-12-31-avoiding-vendor-lock-in.md │ │ │ └── 2021-scoped-css-for-everyone.md │ │ ├── package.json │ │ ├── paperclip.config.json │ │ ├── plugins/ │ │ │ ├── live-editor.js │ │ │ ├── paperclip.js │ │ │ ├── shim/ │ │ │ │ └── index.js │ │ │ └── theme/ │ │ │ └── CodeBlock/ │ │ │ └── index.jsx │ │ ├── sidebars.js │ │ ├── src/ │ │ │ ├── css/ │ │ │ │ └── custom.css │ │ │ ├── demos/ │ │ │ │ ├── ice-cream-loader.js │ │ │ │ ├── import-code.js │ │ │ │ ├── main.js │ │ │ │ ├── simple-pc.js │ │ │ │ ├── site-demo.js │ │ │ │ ├── tailwind-and-animate.js │ │ │ │ ├── tailwind.css.js │ │ │ │ └── third-party-css.js │ │ │ ├── pages/ │ │ │ │ ├── index-old.js │ │ │ │ ├── index.js │ │ │ │ ├── repl.js │ │ │ │ └── styles.module.css │ │ │ └── styles/ │ │ │ ├── button.pc │ │ │ ├── colors.pc │ │ │ ├── fonts/ │ │ │ │ ├── open-sans/ │ │ │ │ │ └── font-face.pc │ │ │ │ ├── preview.pc │ │ │ │ ├── roboto/ │ │ │ │ │ └── font-face.pc │ │ │ │ └── sora/ │ │ │ │ └── font-face.pc │ │ │ ├── icons/ │ │ │ │ └── icons.pc │ │ │ ├── index-backend.pc │ │ │ ├── index.pc │ │ │ ├── index2.pc │ │ │ ├── layout.pc │ │ │ ├── test.pc │ │ │ ├── test2.pc │ │ │ ├── test3.pc │ │ │ └── typography.pc │ │ └── static/ │ │ ├── .nojekyll │ │ └── CNAME │ ├── percy-paperclip/ │ │ ├── .eslintrc.json │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bin/ │ │ │ └── percy-paperclip │ │ ├── package.json │ │ ├── src/ │ │ │ ├── cli.ts │ │ │ ├── static-server.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── prettier-plugin-paperclip/ │ │ ├── LICENSE │ │ ├── examples/ │ │ │ ├── test.css │ │ │ └── test.pc │ │ ├── fixtures/ │ │ │ ├── .prettierignore │ │ │ ├── basic-comment-in.pc │ │ │ ├── basic-comment-out.pc │ │ │ ├── basic-in.pc │ │ │ ├── basic-out.pc │ │ │ ├── basic-style-in.pc │ │ │ └── basic-style-out.pc │ │ ├── package.json │ │ ├── src/ │ │ │ ├── embed.ts │ │ │ ├── index.ts │ │ │ ├── options.ts │ │ │ ├── print.ts │ │ │ ├── test/ │ │ │ │ └── print-test.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── tandem-common/ │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── disposable.ts │ │ │ ├── http-server.ts │ │ │ ├── index.ts │ │ │ ├── logger.ts │ │ │ ├── mime.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── tandem-design-system/ │ │ ├── .gitignore │ │ ├── package.json │ │ ├── paperclip.config.json │ │ ├── src/ │ │ │ ├── Button.pc │ │ │ ├── Page.pc │ │ │ ├── TextInput.pc │ │ │ ├── TextInput.tsx │ │ │ ├── atoms.pc │ │ │ ├── index.tsx │ │ │ ├── open-sans/ │ │ │ │ └── font-face.pc │ │ │ └── roboto/ │ │ │ └── index.pc │ │ └── tsconfig.json │ ├── tandem-designer/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── README.md │ │ ├── babel.config.js │ │ ├── brainstorm/ │ │ │ └── ui/ │ │ │ └── index.pc │ │ ├── gitignore │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── paperclip.config.json │ │ ├── src/ │ │ │ ├── __TEST__/ │ │ │ │ ├── basic.test.tsx │ │ │ │ ├── edit.test.tsx │ │ │ │ ├── hotkeys.test.tsx │ │ │ │ ├── ui.test.tsx │ │ │ │ └── utils.ts │ │ │ ├── actions/ │ │ │ │ ├── base.ts │ │ │ │ ├── external-actions.ts │ │ │ │ ├── index.ts │ │ │ │ ├── instance-actions.ts │ │ │ │ ├── server-actions.ts │ │ │ │ ├── ui-actions.ts │ │ │ │ ├── util.ts │ │ │ │ └── workspace-actions.ts │ │ │ ├── app.tsx │ │ │ ├── components/ │ │ │ │ ├── Box/ │ │ │ │ │ └── index.pc │ │ │ │ ├── Field/ │ │ │ │ │ └── index.pc │ │ │ │ ├── FrameContainer/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── Grid/ │ │ │ │ │ └── index.pc │ │ │ │ ├── InfiniteScroller/ │ │ │ │ │ ├── index.pc │ │ │ │ │ └── index.tsx │ │ │ │ ├── List/ │ │ │ │ │ └── index.pc │ │ │ │ ├── Main/ │ │ │ │ │ ├── CodeMode/ │ │ │ │ │ │ ├── MonacoEditor/ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── SlimEditor/ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── prism.css │ │ │ │ │ │ │ ├── theme.css │ │ │ │ │ │ │ └── theme.ts │ │ │ │ │ │ ├── Toolbar/ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── DesignMode/ │ │ │ │ │ │ ├── Birdseye/ │ │ │ │ │ │ │ ├── Cell.tsx │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── Canvas/ │ │ │ │ │ │ │ ├── Frames/ │ │ │ │ │ │ │ │ ├── Frame.tsx │ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── Preview/ │ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── Tools/ │ │ │ │ │ │ │ │ ├── Distance/ │ │ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── Empty/ │ │ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── Frames/ │ │ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── Pixels/ │ │ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── Selectable/ │ │ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── TextEditor/ │ │ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── normalize-wheel.ts │ │ │ │ │ │ ├── ErrorBanner/ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── Footer/ │ │ │ │ │ │ │ ├── Breadcrumbs/ │ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── Layers/ │ │ │ │ │ │ │ │ ├── Children.tsx │ │ │ │ │ │ │ │ ├── Element.tsx │ │ │ │ │ │ │ │ ├── Node.tsx │ │ │ │ │ │ │ │ ├── Text.tsx │ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── LeftSidebar/ │ │ │ │ │ │ │ └── index.pc │ │ │ │ │ │ ├── MediaPreview/ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── Quickfind/ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── RightSidebar/ │ │ │ │ │ │ │ ├── CSSInspector/ │ │ │ │ │ │ │ │ ├── ComputedInspector/ │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ └── squash-inspection.ts │ │ │ │ │ │ │ │ ├── Declaration/ │ │ │ │ │ │ │ │ │ ├── Name.tsx │ │ │ │ │ │ │ │ │ ├── Value.tsx │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ ├── useDeclarationPart.ts │ │ │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ │ │ ├── DeclarationList.tsx │ │ │ │ │ │ │ │ ├── RuleInspector/ │ │ │ │ │ │ │ │ │ ├── StyleRule.tsx │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── inputs/ │ │ │ │ │ │ │ │ └── ColorPicker/ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ ├── styles.pc │ │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ │ ├── Inspector/ │ │ │ │ │ │ │ │ └── index.pc │ │ │ │ │ │ │ ├── MoreStyles/ │ │ │ │ │ │ │ │ └── index.pc │ │ │ │ │ │ │ ├── PrettyStyler/ │ │ │ │ │ │ │ │ └── index.pc │ │ │ │ │ │ │ ├── computed-styles.pc │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── Toolbar/ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── index2.pc │ │ │ │ │ │ ├── WindowResizer/ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── LeftSidebar/ │ │ │ │ │ │ ├── Header/ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── files/ │ │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── index.pc │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── symbols/ │ │ │ │ │ │ └── index.pc │ │ │ │ │ ├── Pane/ │ │ │ │ │ │ └── index.pc │ │ │ │ │ ├── TextEditor/ │ │ │ │ │ │ └── index.pc │ │ │ │ │ ├── create-app-store.ts │ │ │ │ │ ├── index.pc │ │ │ │ │ └── index.tsx │ │ │ │ ├── Modal/ │ │ │ │ │ ├── index.pc │ │ │ │ │ └── index.tsx │ │ │ │ ├── Pane/ │ │ │ │ │ └── index.pc │ │ │ │ ├── Prompt/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── ResizableContainer/ │ │ │ │ │ ├── index.pc │ │ │ │ │ └── index.tsx │ │ │ │ ├── Select/ │ │ │ │ │ ├── index.pc │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── index2.pc │ │ │ │ ├── Spinner/ │ │ │ │ │ ├── index.pc │ │ │ │ │ └── index.tsx │ │ │ │ ├── TextInput/ │ │ │ │ │ ├── base.tsx │ │ │ │ │ ├── blended.pc │ │ │ │ │ ├── blended.tsx │ │ │ │ │ ├── filter.pc │ │ │ │ │ └── index.tsx │ │ │ │ ├── Toggle/ │ │ │ │ │ └── index.pc │ │ │ │ ├── Tooltip/ │ │ │ │ │ └── index.pc │ │ │ │ ├── Tree/ │ │ │ │ │ ├── index.pc │ │ │ │ │ └── index.tsx │ │ │ │ └── icons/ │ │ │ │ └── index.pc │ │ │ ├── constants/ │ │ │ │ └── index.ts │ │ │ ├── contexts/ │ │ │ │ └── index.ts │ │ │ ├── engines/ │ │ │ │ ├── base.ts │ │ │ │ ├── index.ts │ │ │ │ ├── utils.ts │ │ │ │ └── workspace/ │ │ │ │ ├── index.ts │ │ │ │ ├── managers/ │ │ │ │ │ ├── documents.ts │ │ │ │ │ ├── edit.ts │ │ │ │ │ ├── main-document.ts │ │ │ │ │ ├── paperclip-engine.ts │ │ │ │ │ └── project.ts │ │ │ │ └── utils.ts │ │ │ ├── entry.tsx │ │ │ ├── global.d.ts │ │ │ ├── hooks/ │ │ │ │ ├── useAppStore/ │ │ │ │ │ └── index.ts │ │ │ │ ├── useCache/ │ │ │ │ │ └── index.ts │ │ │ │ ├── useDragger/ │ │ │ │ │ └── index.ts │ │ │ │ ├── useFrame/ │ │ │ │ │ └── index.ts │ │ │ │ ├── useFrameContainer/ │ │ │ │ │ └── index.ts │ │ │ │ ├── useFrameMount/ │ │ │ │ │ └── index.ts │ │ │ │ └── useFrameUrlResolver/ │ │ │ │ └── index.ts │ │ │ ├── index.html │ │ │ ├── index.ts │ │ │ ├── reducers/ │ │ │ │ ├── designer.ts │ │ │ │ ├── history.ts │ │ │ │ ├── index.ts │ │ │ │ └── shared.ts │ │ │ ├── rpc/ │ │ │ │ └── channels.ts │ │ │ ├── sagas/ │ │ │ │ ├── canvas.ts │ │ │ │ ├── hotkeys.ts │ │ │ │ ├── index.ts │ │ │ │ ├── rpc/ │ │ │ │ │ ├── channels.ts │ │ │ │ │ ├── connection.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── ui.ts │ │ │ │ ├── utils.ts │ │ │ │ └── workspace/ │ │ │ │ └── index.ts │ │ │ ├── state/ │ │ │ │ ├── geom.ts │ │ │ │ ├── index.ts │ │ │ │ └── result.ts │ │ │ ├── styles/ │ │ │ │ ├── atoms.pc │ │ │ │ ├── index.pc │ │ │ │ └── utils.pc │ │ │ └── utils/ │ │ │ ├── dnd.ts │ │ │ └── index.ts │ │ ├── test/ │ │ │ └── jest.setup.js │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── tandem-workspace/ │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── bin/ │ │ │ └── tandem-workspace │ │ ├── examples/ │ │ │ └── todo/ │ │ │ └── index.pc │ │ ├── package.json │ │ ├── src/ │ │ │ ├── controllers/ │ │ │ │ ├── designer.ts │ │ │ │ ├── git.ts │ │ │ │ ├── package.ts │ │ │ │ ├── paperclip.ts │ │ │ │ ├── project.ts │ │ │ │ ├── rpc.ts │ │ │ │ ├── ssh.ts │ │ │ │ ├── vfs.ts │ │ │ │ └── workspace.ts │ │ │ ├── core/ │ │ │ │ ├── kernel.ts │ │ │ │ └── options.ts │ │ │ ├── index.ts │ │ │ ├── routes/ │ │ │ │ └── index.ts │ │ │ ├── server.ts │ │ │ ├── test/ │ │ │ │ ├── basic-test.ts │ │ │ │ └── utils.ts │ │ │ └── todos.pc │ │ └── tsconfig.json │ ├── tandem-workspace-client/ │ │ ├── package.json │ │ ├── src/ │ │ │ ├── connection.ts │ │ │ ├── index.ts │ │ │ ├── paperclip.ts │ │ │ └── project.ts │ │ └── tsconfig.json │ ├── tandem-workspace-core/ │ │ ├── package.json │ │ ├── src/ │ │ │ ├── channels.ts │ │ │ ├── index.ts │ │ │ └── state.ts │ │ └── tsconfig.json │ └── zeplin-paperclip/ │ ├── .eslintrc.json │ ├── .npmignore │ ├── LICENSE │ ├── README.md │ ├── bin/ │ │ └── zeplin-paperclip │ ├── examples/ │ │ └── test/ │ │ ├── .gitignore │ │ └── package.json │ ├── package.json │ ├── src/ │ │ ├── api.ts │ │ ├── cast.ts │ │ ├── cli.ts │ │ ├── contants.ts │ │ ├── pc-compiler.ts │ │ ├── pull.ts │ │ ├── state.ts │ │ └── utils.ts │ └── tsconfig.json └── todos.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/checks.yml ================================================ name: Checks on: push: branches: - "*" jobs: test: strategy: matrix: os: [macos-latest, ubuntu-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: node-version: "14.x" - uses: jetli/wasm-pack-action@v0.3.0 - run: yarn install - run: yarn build - run: yarn test lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: node-version: "14.x" - run: yarn install - run: yarn lint ================================================ FILE: .github/workflows/documentation.yml ================================================ name: Publish Documentation on: push: branches: - master jobs: publish-documentation: name: Publish documentation runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: node-version: "14.x" - uses: jetli/wasm-pack-action@v0.3.0 - run: yarn install - run: yarn run build - run: yarn run link - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: packages/paperclip-website/build ================================================ FILE: .github/workflows/publish.yml ================================================ name: Publish Packages on: push: branches: - master jobs: publish-packages: name: Publish NPM & Marketplaces runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: node-version: "14.x" registry-url: https://registry.npmjs.org/ - uses: jetli/wasm-pack-action@v0.3.0 - run: yarn install - run: yarn build - run: yarn publish:packages env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_ENV: "production" # wait for a bit for NPM packages to show up in registry - name: Sleep for a bit uses: jakejarvis/wait-action@master with: time: "120s" - run: yarn add vsce -W - run: yarn publish:marketplace env: VSCE_TOKEN: ${{ secrets.VSCE_TOKEN }} name: documentation ================================================ FILE: .gitignore ================================================ lib/ target/ node_modules/ dist/ *.node yarn-*.log *.artifacts tmp/ *.pc.d.ts *.pc.js *.pc.css *.vsix *.DS_Store lerna-debug.log paperclip-html.tmLanguage.json .yarn/* *.map .yarn/cache !.yarn/patches !.yarn/plugins !.yarn/releases !.yarn/sdks !.yarn/versions .paperclip *.tgz *.mjs pc-coverage pc-diff ================================================ FILE: .husky/pre-commit ================================================ #!/bin/sh . "$(dirname "$0")/_/husky.sh" yarn pretty-quick --staged ================================================ FILE: .prettierignore ================================================ *.md lerna.json packages/prettier-plugin-paperclip/**/*.pc ================================================ FILE: .vscode/launch.json ================================================ { "version": "0.1.0", "configurations": [ { "name": "Paperclip", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}/packages/paperclip-vscode", "args": [ "--extensionDevelopmentPath=${workspaceFolder}/packages/paperclip-vscode" ] }, { "name": "Paperclip Tests", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}/packages/paperclip-vscode", "args": [ "${workspaceFolder}/packages/paperclip-vscode/src/test/all/fixtures", "--extensionDevelopmentPath=${workspaceFolder}/packages/paperclip-vscode", "--extensionTestsPath=${workspaceFolder}/packages/paperclip-vscode/lib/test" ], "outFiles": [ "${workspaceFolder}/packages/paperclip-vscode/lib/test/**/*-test.js" ] } ] } ================================================ FILE: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs ================================================ /* eslint-disable */ //prettier-ignore module.exports = { name: "@yarnpkg/plugin-workspace-tools", factory: function (require) { var plugin=(()=>{var wr=Object.create,ge=Object.defineProperty,Sr=Object.defineProperties,vr=Object.getOwnPropertyDescriptor,Hr=Object.getOwnPropertyDescriptors,$r=Object.getOwnPropertyNames,Je=Object.getOwnPropertySymbols,kr=Object.getPrototypeOf,et=Object.prototype.hasOwnProperty,Tr=Object.prototype.propertyIsEnumerable;var tt=(e,t,r)=>t in e?ge(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,I=(e,t)=>{for(var r in t||(t={}))et.call(t,r)&&tt(e,r,t[r]);if(Je)for(var r of Je(t))Tr.call(t,r)&&tt(e,r,t[r]);return e},F=(e,t)=>Sr(e,Hr(t)),Lr=e=>ge(e,"__esModule",{value:!0});var K=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Or=(e,t)=>{for(var r in t)ge(e,r,{get:t[r],enumerable:!0})},Nr=(e,t,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of $r(t))!et.call(e,n)&&n!=="default"&&ge(e,n,{get:()=>t[n],enumerable:!(r=vr(t,n))||r.enumerable});return e},Q=e=>Nr(Lr(ge(e!=null?wr(kr(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);var He=K(ee=>{"use strict";ee.isInteger=e=>typeof e=="number"?Number.isInteger(e):typeof e=="string"&&e.trim()!==""?Number.isInteger(Number(e)):!1;ee.find=(e,t)=>e.nodes.find(r=>r.type===t);ee.exceedsLimit=(e,t,r=1,n)=>n===!1||!ee.isInteger(e)||!ee.isInteger(t)?!1:(Number(t)-Number(e))/Number(r)>=n;ee.escapeNode=(e,t=0,r)=>{let n=e.nodes[t];!n||(r&&n.type===r||n.type==="open"||n.type==="close")&&n.escaped!==!0&&(n.value="\\"+n.value,n.escaped=!0)};ee.encloseBrace=e=>e.type!=="brace"?!1:e.commas>>0+e.ranges>>0==0?(e.invalid=!0,!0):!1;ee.isInvalidBrace=e=>e.type!=="brace"?!1:e.invalid===!0||e.dollar?!0:e.commas>>0+e.ranges>>0==0||e.open!==!0||e.close!==!0?(e.invalid=!0,!0):!1;ee.isOpenOrClose=e=>e.type==="open"||e.type==="close"?!0:e.open===!0||e.close===!0;ee.reduce=e=>e.reduce((t,r)=>(r.type==="text"&&t.push(r.value),r.type==="range"&&(r.type="text"),t),[]);ee.flatten=(...e)=>{let t=[],r=n=>{for(let s=0;s{"use strict";var at=He();st.exports=(e,t={})=>{let r=(n,s={})=>{let a=t.escapeInvalid&&at.isInvalidBrace(s),i=n.invalid===!0&&t.escapeInvalid===!0,o="";if(n.value)return(a||i)&&at.isOpenOrClose(n)?"\\"+n.value:n.value;if(n.value)return n.value;if(n.nodes)for(let h of n.nodes)o+=r(h);return o};return r(e)}});var ot=K((os,it)=>{"use strict";it.exports=function(e){return typeof e=="number"?e-e==0:typeof e=="string"&&e.trim()!==""?Number.isFinite?Number.isFinite(+e):isFinite(+e):!1}});var mt=K((us,ut)=>{"use strict";var ct=ot(),pe=(e,t,r)=>{if(ct(e)===!1)throw new TypeError("toRegexRange: expected the first argument to be a number");if(t===void 0||e===t)return String(e);if(ct(t)===!1)throw new TypeError("toRegexRange: expected the second argument to be a number.");let n=I({relaxZeros:!0},r);typeof n.strictZeros=="boolean"&&(n.relaxZeros=n.strictZeros===!1);let s=String(n.relaxZeros),a=String(n.shorthand),i=String(n.capture),o=String(n.wrap),h=e+":"+t+"="+s+a+i+o;if(pe.cache.hasOwnProperty(h))return pe.cache[h].result;let m=Math.min(e,t),f=Math.max(e,t);if(Math.abs(m-f)===1){let y=e+"|"+t;return n.capture?`(${y})`:n.wrap===!1?y:`(?:${y})`}let R=pt(e)||pt(t),p={min:e,max:t,a:m,b:f},v=[],_=[];if(R&&(p.isPadded=R,p.maxLen=String(p.max).length),m<0){let y=f<0?Math.abs(f):1;_=lt(y,Math.abs(m),p,n),m=p.a=0}return f>=0&&(v=lt(m,f,p,n)),p.negatives=_,p.positives=v,p.result=Ir(_,v,n),n.capture===!0?p.result=`(${p.result})`:n.wrap!==!1&&v.length+_.length>1&&(p.result=`(?:${p.result})`),pe.cache[h]=p,p.result};function Ir(e,t,r){let n=Pe(e,t,"-",!1,r)||[],s=Pe(t,e,"",!1,r)||[],a=Pe(e,t,"-?",!0,r)||[];return n.concat(a).concat(s).join("|")}function Mr(e,t){let r=1,n=1,s=ft(e,r),a=new Set([t]);for(;e<=s&&s<=t;)a.add(s),r+=1,s=ft(e,r);for(s=ht(t+1,n)-1;e1&&o.count.pop(),o.count.push(f.count[0]),o.string=o.pattern+dt(o.count),i=m+1;continue}r.isPadded&&(R=Ur(m,r,n)),f.string=R+f.pattern+dt(f.count),a.push(f),i=m+1,o=f}return a}function Pe(e,t,r,n,s){let a=[];for(let i of e){let{string:o}=i;!n&&!gt(t,"string",o)&&a.push(r+o),n&>(t,"string",o)&&a.push(r+o)}return a}function Pr(e,t){let r=[];for(let n=0;nt?1:t>e?-1:0}function gt(e,t,r){return e.some(n=>n[t]===r)}function ft(e,t){return Number(String(e).slice(0,-t)+"9".repeat(t))}function ht(e,t){return e-e%Math.pow(10,t)}function dt(e){let[t=0,r=""]=e;return r||t>1?`{${t+(r?","+r:"")}}`:""}function Dr(e,t,r){return`[${e}${t-e==1?"":"-"}${t}]`}function pt(e){return/^-?(0+)\d/.test(e)}function Ur(e,t,r){if(!t.isPadded)return e;let n=Math.abs(t.maxLen-String(e).length),s=r.relaxZeros!==!1;switch(n){case 0:return"";case 1:return s?"0?":"0";case 2:return s?"0{0,2}":"00";default:return s?`0{0,${n}}`:`0{${n}}`}}pe.cache={};pe.clearCache=()=>pe.cache={};ut.exports=pe});var Ue=K((cs,At)=>{"use strict";var qr=require("util"),Rt=mt(),yt=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),Kr=e=>t=>e===!0?Number(t):String(t),De=e=>typeof e=="number"||typeof e=="string"&&e!=="",Ae=e=>Number.isInteger(+e),Ge=e=>{let t=`${e}`,r=-1;if(t[0]==="-"&&(t=t.slice(1)),t==="0")return!1;for(;t[++r]==="0";);return r>0},Wr=(e,t,r)=>typeof e=="string"||typeof t=="string"?!0:r.stringify===!0,jr=(e,t,r)=>{if(t>0){let n=e[0]==="-"?"-":"";n&&(e=e.slice(1)),e=n+e.padStart(n?t-1:t,"0")}return r===!1?String(e):e},_t=(e,t)=>{let r=e[0]==="-"?"-":"";for(r&&(e=e.slice(1),t--);e.length{e.negatives.sort((i,o)=>io?1:0),e.positives.sort((i,o)=>io?1:0);let r=t.capture?"":"?:",n="",s="",a;return e.positives.length&&(n=e.positives.join("|")),e.negatives.length&&(s=`-(${r}${e.negatives.join("|")})`),n&&s?a=`${n}|${s}`:a=n||s,t.wrap?`(${r}${a})`:a},bt=(e,t,r,n)=>{if(r)return Rt(e,t,I({wrap:!1},n));let s=String.fromCharCode(e);if(e===t)return s;let a=String.fromCharCode(t);return`[${s}-${a}]`},Et=(e,t,r)=>{if(Array.isArray(e)){let n=r.wrap===!0,s=r.capture?"":"?:";return n?`(${s}${e.join("|")})`:e.join("|")}return Rt(e,t,r)},xt=(...e)=>new RangeError("Invalid range arguments: "+qr.inspect(...e)),Ct=(e,t,r)=>{if(r.strictRanges===!0)throw xt([e,t]);return[]},Qr=(e,t)=>{if(t.strictRanges===!0)throw new TypeError(`Expected step "${e}" to be a number`);return[]},Xr=(e,t,r=1,n={})=>{let s=Number(e),a=Number(t);if(!Number.isInteger(s)||!Number.isInteger(a)){if(n.strictRanges===!0)throw xt([e,t]);return[]}s===0&&(s=0),a===0&&(a=0);let i=s>a,o=String(e),h=String(t),m=String(r);r=Math.max(Math.abs(r),1);let f=Ge(o)||Ge(h)||Ge(m),R=f?Math.max(o.length,h.length,m.length):0,p=f===!1&&Wr(e,t,n)===!1,v=n.transform||Kr(p);if(n.toRegex&&r===1)return bt(_t(e,R),_t(t,R),!0,n);let _={negatives:[],positives:[]},y=H=>_[H<0?"negatives":"positives"].push(Math.abs(H)),b=[],E=0;for(;i?s>=a:s<=a;)n.toRegex===!0&&r>1?y(s):b.push(jr(v(s,E),R,p)),s=i?s-r:s+r,E++;return n.toRegex===!0?r>1?Fr(_,n):Et(b,null,I({wrap:!1},n)):b},Zr=(e,t,r=1,n={})=>{if(!Ae(e)&&e.length>1||!Ae(t)&&t.length>1)return Ct(e,t,n);let s=n.transform||(p=>String.fromCharCode(p)),a=`${e}`.charCodeAt(0),i=`${t}`.charCodeAt(0),o=a>i,h=Math.min(a,i),m=Math.max(a,i);if(n.toRegex&&r===1)return bt(h,m,!1,n);let f=[],R=0;for(;o?a>=i:a<=i;)f.push(s(a,R)),a=o?a-r:a+r,R++;return n.toRegex===!0?Et(f,null,{wrap:!1,options:n}):f},ke=(e,t,r,n={})=>{if(t==null&&De(e))return[e];if(!De(e)||!De(t))return Ct(e,t,n);if(typeof r=="function")return ke(e,t,1,{transform:r});if(yt(r))return ke(e,t,0,r);let s=I({},n);return s.capture===!0&&(s.wrap=!0),r=r||s.step||1,Ae(r)?Ae(e)&&Ae(t)?Xr(e,t,r,s):Zr(e,t,Math.max(Math.abs(r),1),s):r!=null&&!yt(r)?Qr(r,s):ke(e,t,1,r)};At.exports=ke});var vt=K((ls,wt)=>{"use strict";var Yr=Ue(),St=He(),zr=(e,t={})=>{let r=(n,s={})=>{let a=St.isInvalidBrace(s),i=n.invalid===!0&&t.escapeInvalid===!0,o=a===!0||i===!0,h=t.escapeInvalid===!0?"\\":"",m="";if(n.isOpen===!0||n.isClose===!0)return h+n.value;if(n.type==="open")return o?h+n.value:"(";if(n.type==="close")return o?h+n.value:")";if(n.type==="comma")return n.prev.type==="comma"?"":o?n.value:"|";if(n.value)return n.value;if(n.nodes&&n.ranges>0){let f=St.reduce(n.nodes),R=Yr(...f,F(I({},t),{wrap:!1,toRegex:!0}));if(R.length!==0)return f.length>1&&R.length>1?`(${R})`:R}if(n.nodes)for(let f of n.nodes)m+=r(f,n);return m};return r(e)};wt.exports=zr});var kt=K((ps,Ht)=>{"use strict";var Vr=Ue(),$t=$e(),he=He(),fe=(e="",t="",r=!1)=>{let n=[];if(e=[].concat(e),t=[].concat(t),!t.length)return e;if(!e.length)return r?he.flatten(t).map(s=>`{${s}}`):t;for(let s of e)if(Array.isArray(s))for(let a of s)n.push(fe(a,t,r));else for(let a of t)r===!0&&typeof a=="string"&&(a=`{${a}}`),n.push(Array.isArray(a)?fe(s,a,r):s+a);return he.flatten(n)},Jr=(e,t={})=>{let r=t.rangeLimit===void 0?1e3:t.rangeLimit,n=(s,a={})=>{s.queue=[];let i=a,o=a.queue;for(;i.type!=="brace"&&i.type!=="root"&&i.parent;)i=i.parent,o=i.queue;if(s.invalid||s.dollar){o.push(fe(o.pop(),$t(s,t)));return}if(s.type==="brace"&&s.invalid!==!0&&s.nodes.length===2){o.push(fe(o.pop(),["{}"]));return}if(s.nodes&&s.ranges>0){let R=he.reduce(s.nodes);if(he.exceedsLimit(...R,t.step,r))throw new RangeError("expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.");let p=Vr(...R,t);p.length===0&&(p=$t(s,t)),o.push(fe(o.pop(),p)),s.nodes=[];return}let h=he.encloseBrace(s),m=s.queue,f=s;for(;f.type!=="brace"&&f.type!=="root"&&f.parent;)f=f.parent,m=f.queue;for(let R=0;R{"use strict";Tt.exports={MAX_LENGTH:1024*64,CHAR_0:"0",CHAR_9:"9",CHAR_UPPERCASE_A:"A",CHAR_LOWERCASE_A:"a",CHAR_UPPERCASE_Z:"Z",CHAR_LOWERCASE_Z:"z",CHAR_LEFT_PARENTHESES:"(",CHAR_RIGHT_PARENTHESES:")",CHAR_ASTERISK:"*",CHAR_AMPERSAND:"&",CHAR_AT:"@",CHAR_BACKSLASH:"\\",CHAR_BACKTICK:"`",CHAR_CARRIAGE_RETURN:"\r",CHAR_CIRCUMFLEX_ACCENT:"^",CHAR_COLON:":",CHAR_COMMA:",",CHAR_DOLLAR:"$",CHAR_DOT:".",CHAR_DOUBLE_QUOTE:'"',CHAR_EQUAL:"=",CHAR_EXCLAMATION_MARK:"!",CHAR_FORM_FEED:"\f",CHAR_FORWARD_SLASH:"/",CHAR_HASH:"#",CHAR_HYPHEN_MINUS:"-",CHAR_LEFT_ANGLE_BRACKET:"<",CHAR_LEFT_CURLY_BRACE:"{",CHAR_LEFT_SQUARE_BRACKET:"[",CHAR_LINE_FEED:` `,CHAR_NO_BREAK_SPACE:"\xA0",CHAR_PERCENT:"%",CHAR_PLUS:"+",CHAR_QUESTION_MARK:"?",CHAR_RIGHT_ANGLE_BRACKET:">",CHAR_RIGHT_CURLY_BRACE:"}",CHAR_RIGHT_SQUARE_BRACKET:"]",CHAR_SEMICOLON:";",CHAR_SINGLE_QUOTE:"'",CHAR_SPACE:" ",CHAR_TAB:" ",CHAR_UNDERSCORE:"_",CHAR_VERTICAL_LINE:"|",CHAR_ZERO_WIDTH_NOBREAK_SPACE:"\uFEFF"}});var Mt=K((hs,Ot)=>{"use strict";var en=$e(),{MAX_LENGTH:Nt,CHAR_BACKSLASH:qe,CHAR_BACKTICK:tn,CHAR_COMMA:rn,CHAR_DOT:nn,CHAR_LEFT_PARENTHESES:sn,CHAR_RIGHT_PARENTHESES:an,CHAR_LEFT_CURLY_BRACE:on,CHAR_RIGHT_CURLY_BRACE:un,CHAR_LEFT_SQUARE_BRACKET:It,CHAR_RIGHT_SQUARE_BRACKET:Bt,CHAR_DOUBLE_QUOTE:cn,CHAR_SINGLE_QUOTE:ln,CHAR_NO_BREAK_SPACE:pn,CHAR_ZERO_WIDTH_NOBREAK_SPACE:fn}=Lt(),hn=(e,t={})=>{if(typeof e!="string")throw new TypeError("Expected a string");let r=t||{},n=typeof r.maxLength=="number"?Math.min(Nt,r.maxLength):Nt;if(e.length>n)throw new SyntaxError(`Input length (${e.length}), exceeds max characters (${n})`);let s={type:"root",input:e,nodes:[]},a=[s],i=s,o=s,h=0,m=e.length,f=0,R=0,p,v={},_=()=>e[f++],y=b=>{if(b.type==="text"&&o.type==="dot"&&(o.type="text"),o&&o.type==="text"&&b.type==="text"){o.value+=b.value;return}return i.nodes.push(b),b.parent=i,b.prev=o,o=b,b};for(y({type:"bos"});f0){if(i.ranges>0){i.ranges=0;let b=i.nodes.shift();i.nodes=[b,{type:"text",value:en(i)}]}y({type:"comma",value:p}),i.commas++;continue}if(p===nn&&R>0&&i.commas===0){let b=i.nodes;if(R===0||b.length===0){y({type:"text",value:p});continue}if(o.type==="dot"){if(i.range=[],o.value+=p,o.type="range",i.nodes.length!==3&&i.nodes.length!==5){i.invalid=!0,i.ranges=0,o.type="text";continue}i.ranges++,i.args=[];continue}if(o.type==="range"){b.pop();let E=b[b.length-1];E.value+=o.value+p,o=E,i.ranges--;continue}y({type:"dot",value:p});continue}y({type:"text",value:p})}do if(i=a.pop(),i.type!=="root"){i.nodes.forEach(H=>{H.nodes||(H.type==="open"&&(H.isOpen=!0),H.type==="close"&&(H.isClose=!0),H.nodes||(H.type="text"),H.invalid=!0)});let b=a[a.length-1],E=b.nodes.indexOf(i);b.nodes.splice(E,1,...i.nodes)}while(a.length>0);return y({type:"eos"}),s};Ot.exports=hn});var Gt=K((ds,Pt)=>{"use strict";var Dt=$e(),dn=vt(),gn=kt(),mn=Mt(),z=(e,t={})=>{let r=[];if(Array.isArray(e))for(let n of e){let s=z.create(n,t);Array.isArray(s)?r.push(...s):r.push(s)}else r=[].concat(z.create(e,t));return t&&t.expand===!0&&t.nodupes===!0&&(r=[...new Set(r)]),r};z.parse=(e,t={})=>mn(e,t);z.stringify=(e,t={})=>typeof e=="string"?Dt(z.parse(e,t),t):Dt(e,t);z.compile=(e,t={})=>(typeof e=="string"&&(e=z.parse(e,t)),dn(e,t));z.expand=(e,t={})=>{typeof e=="string"&&(e=z.parse(e,t));let r=gn(e,t);return t.noempty===!0&&(r=r.filter(Boolean)),t.nodupes===!0&&(r=[...new Set(r)]),r};z.create=(e,t={})=>e===""||e.length<3?[e]:t.expand!==!0?z.compile(e,t):z.expand(e,t);Pt.exports=z});var Re=K((gs,Ut)=>{"use strict";var An=require("path"),se="\\\\/",qt=`[^${se}]`,ue="\\.",Rn="\\+",yn="\\?",Te="\\/",_n="(?=.)",Kt="[^/]",Ke=`(?:${Te}|$)`,Wt=`(?:^|${Te})`,We=`${ue}{1,2}${Ke}`,bn=`(?!${ue})`,En=`(?!${Wt}${We})`,xn=`(?!${ue}{0,1}${Ke})`,Cn=`(?!${We})`,wn=`[^.${Te}]`,Sn=`${Kt}*?`,jt={DOT_LITERAL:ue,PLUS_LITERAL:Rn,QMARK_LITERAL:yn,SLASH_LITERAL:Te,ONE_CHAR:_n,QMARK:Kt,END_ANCHOR:Ke,DOTS_SLASH:We,NO_DOT:bn,NO_DOTS:En,NO_DOT_SLASH:xn,NO_DOTS_SLASH:Cn,QMARK_NO_DOT:wn,STAR:Sn,START_ANCHOR:Wt},vn=F(I({},jt),{SLASH_LITERAL:`[${se}]`,QMARK:qt,STAR:`${qt}*?`,DOTS_SLASH:`${ue}{1,2}(?:[${se}]|$)`,NO_DOT:`(?!${ue})`,NO_DOTS:`(?!(?:^|[${se}])${ue}{1,2}(?:[${se}]|$))`,NO_DOT_SLASH:`(?!${ue}{0,1}(?:[${se}]|$))`,NO_DOTS_SLASH:`(?!${ue}{1,2}(?:[${se}]|$))`,QMARK_NO_DOT:`[^.${se}]`,START_ANCHOR:`(?:^|[${se}])`,END_ANCHOR:`(?:[${se}]|$)`}),Hn={alnum:"a-zA-Z0-9",alpha:"a-zA-Z",ascii:"\\x00-\\x7F",blank:" \\t",cntrl:"\\x00-\\x1F\\x7F",digit:"0-9",graph:"\\x21-\\x7E",lower:"a-z",print:"\\x20-\\x7E ",punct:"\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~",space:" \\t\\r\\n\\v\\f",upper:"A-Z",word:"A-Za-z0-9_",xdigit:"A-Fa-f0-9"};Ut.exports={MAX_LENGTH:1024*64,POSIX_REGEX_SOURCE:Hn,REGEX_BACKSLASH:/\\(?![*+?^${}(|)[\]])/g,REGEX_NON_SPECIAL_CHARS:/^[^@![\].,$*+?^{}()|\\/]+/,REGEX_SPECIAL_CHARS:/[-*+?.^${}(|)[\]]/,REGEX_SPECIAL_CHARS_BACKREF:/(\\?)((\W)(\3*))/g,REGEX_SPECIAL_CHARS_GLOBAL:/([-*+?.^${}(|)[\]])/g,REGEX_REMOVE_BACKSLASH:/(?:\[.*?[^\\]\]|\\(?=.))/g,REPLACEMENTS:{"***":"*","**/**":"**","**/**/**":"**"},CHAR_0:48,CHAR_9:57,CHAR_UPPERCASE_A:65,CHAR_LOWERCASE_A:97,CHAR_UPPERCASE_Z:90,CHAR_LOWERCASE_Z:122,CHAR_LEFT_PARENTHESES:40,CHAR_RIGHT_PARENTHESES:41,CHAR_ASTERISK:42,CHAR_AMPERSAND:38,CHAR_AT:64,CHAR_BACKWARD_SLASH:92,CHAR_CARRIAGE_RETURN:13,CHAR_CIRCUMFLEX_ACCENT:94,CHAR_COLON:58,CHAR_COMMA:44,CHAR_DOT:46,CHAR_DOUBLE_QUOTE:34,CHAR_EQUAL:61,CHAR_EXCLAMATION_MARK:33,CHAR_FORM_FEED:12,CHAR_FORWARD_SLASH:47,CHAR_GRAVE_ACCENT:96,CHAR_HASH:35,CHAR_HYPHEN_MINUS:45,CHAR_LEFT_ANGLE_BRACKET:60,CHAR_LEFT_CURLY_BRACE:123,CHAR_LEFT_SQUARE_BRACKET:91,CHAR_LINE_FEED:10,CHAR_NO_BREAK_SPACE:160,CHAR_PERCENT:37,CHAR_PLUS:43,CHAR_QUESTION_MARK:63,CHAR_RIGHT_ANGLE_BRACKET:62,CHAR_RIGHT_CURLY_BRACE:125,CHAR_RIGHT_SQUARE_BRACKET:93,CHAR_SEMICOLON:59,CHAR_SINGLE_QUOTE:39,CHAR_SPACE:32,CHAR_TAB:9,CHAR_UNDERSCORE:95,CHAR_VERTICAL_LINE:124,CHAR_ZERO_WIDTH_NOBREAK_SPACE:65279,SEP:An.sep,extglobChars(e){return{"!":{type:"negate",open:"(?:(?!(?:",close:`))${e.STAR})`},"?":{type:"qmark",open:"(?:",close:")?"},"+":{type:"plus",open:"(?:",close:")+"},"*":{type:"star",open:"(?:",close:")*"},"@":{type:"at",open:"(?:",close:")"}}},globChars(e){return e===!0?vn:jt}}});var ye=K(X=>{"use strict";var $n=require("path"),kn=process.platform==="win32",{REGEX_BACKSLASH:Tn,REGEX_REMOVE_BACKSLASH:Ln,REGEX_SPECIAL_CHARS:On,REGEX_SPECIAL_CHARS_GLOBAL:Nn}=Re();X.isObject=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);X.hasRegexChars=e=>On.test(e);X.isRegexChar=e=>e.length===1&&X.hasRegexChars(e);X.escapeRegex=e=>e.replace(Nn,"\\$1");X.toPosixSlashes=e=>e.replace(Tn,"/");X.removeBackslashes=e=>e.replace(Ln,t=>t==="\\"?"":t);X.supportsLookbehinds=()=>{let e=process.version.slice(1).split(".").map(Number);return e.length===3&&e[0]>=9||e[0]===8&&e[1]>=10};X.isWindows=e=>e&&typeof e.windows=="boolean"?e.windows:kn===!0||$n.sep==="\\";X.escapeLast=(e,t,r)=>{let n=e.lastIndexOf(t,r);return n===-1?e:e[n-1]==="\\"?X.escapeLast(e,t,n-1):`${e.slice(0,n)}\\${e.slice(n)}`};X.removePrefix=(e,t={})=>{let r=e;return r.startsWith("./")&&(r=r.slice(2),t.prefix="./"),r};X.wrapOutput=(e,t={},r={})=>{let n=r.contains?"":"^",s=r.contains?"":"$",a=`${n}(?:${e})${s}`;return t.negated===!0&&(a=`(?:^(?!${a}).*$)`),a}});var er=K((As,Ft)=>{"use strict";var Qt=ye(),{CHAR_ASTERISK:je,CHAR_AT:In,CHAR_BACKWARD_SLASH:_e,CHAR_COMMA:Bn,CHAR_DOT:Fe,CHAR_EXCLAMATION_MARK:Xt,CHAR_FORWARD_SLASH:Zt,CHAR_LEFT_CURLY_BRACE:Qe,CHAR_LEFT_PARENTHESES:Xe,CHAR_LEFT_SQUARE_BRACKET:Mn,CHAR_PLUS:Pn,CHAR_QUESTION_MARK:Yt,CHAR_RIGHT_CURLY_BRACE:Dn,CHAR_RIGHT_PARENTHESES:zt,CHAR_RIGHT_SQUARE_BRACKET:Gn}=Re(),Vt=e=>e===Zt||e===_e,Jt=e=>{e.isPrefix!==!0&&(e.depth=e.isGlobstar?Infinity:1)},Un=(e,t)=>{let r=t||{},n=e.length-1,s=r.parts===!0||r.scanToEnd===!0,a=[],i=[],o=[],h=e,m=-1,f=0,R=0,p=!1,v=!1,_=!1,y=!1,b=!1,E=!1,H=!1,L=!1,k=!1,J=0,ie,g,w={value:"",depth:0,isGlob:!1},D=()=>m>=n,W=()=>h.charCodeAt(m+1),l=()=>(ie=g,h.charCodeAt(++m));for(;m0&&(T=h.slice(0,f),h=h.slice(f),R-=f),x&&_===!0&&R>0?(x=h.slice(0,R),U=h.slice(R)):_===!0?(x="",U=h):x=h,x&&x!==""&&x!=="/"&&x!==h&&Vt(x.charCodeAt(x.length-1))&&(x=x.slice(0,-1)),r.unescape===!0&&(U&&(U=Qt.removeBackslashes(U)),x&&H===!0&&(x=Qt.removeBackslashes(x)));let u={prefix:T,input:e,start:f,base:x,glob:U,isBrace:p,isBracket:v,isGlob:_,isExtglob:y,isGlobstar:b,negated:L};if(r.tokens===!0&&(u.maxDepth=0,Vt(g)||i.push(w),u.tokens=i),r.parts===!0||r.tokens===!0){let c;for(let $=0;${"use strict";var Le=Re(),V=ye(),{MAX_LENGTH:Oe,POSIX_REGEX_SOURCE:qn,REGEX_NON_SPECIAL_CHARS:Kn,REGEX_SPECIAL_CHARS_BACKREF:Wn,REPLACEMENTS:rr}=Le,jn=(e,t)=>{if(typeof t.expandRange=="function")return t.expandRange(...e,t);e.sort();let r=`[${e.join("-")}]`;try{new RegExp(r)}catch(n){return e.map(s=>V.escapeRegex(s)).join("..")}return r},de=(e,t)=>`Missing ${e}: "${t}" - use "\\\\${t}" to match literal characters`,nr=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");e=rr[e]||e;let r=I({},t),n=typeof r.maxLength=="number"?Math.min(Oe,r.maxLength):Oe,s=e.length;if(s>n)throw new SyntaxError(`Input length: ${s}, exceeds maximum allowed length: ${n}`);let a={type:"bos",value:"",output:r.prepend||""},i=[a],o=r.capture?"":"?:",h=V.isWindows(t),m=Le.globChars(h),f=Le.extglobChars(m),{DOT_LITERAL:R,PLUS_LITERAL:p,SLASH_LITERAL:v,ONE_CHAR:_,DOTS_SLASH:y,NO_DOT:b,NO_DOT_SLASH:E,NO_DOTS_SLASH:H,QMARK:L,QMARK_NO_DOT:k,STAR:J,START_ANCHOR:ie}=m,g=A=>`(${o}(?:(?!${ie}${A.dot?y:R}).)*?)`,w=r.dot?"":b,D=r.dot?L:k,W=r.bash===!0?g(r):J;r.capture&&(W=`(${W})`),typeof r.noext=="boolean"&&(r.noextglob=r.noext);let l={input:e,index:-1,start:0,dot:r.dot===!0,consumed:"",output:"",prefix:"",backtrack:!1,negated:!1,brackets:0,braces:0,parens:0,quotes:0,globstar:!1,tokens:i};e=V.removePrefix(e,l),s=e.length;let x=[],T=[],U=[],u=a,c,$=()=>l.index===s-1,B=l.peek=(A=1)=>e[l.index+A],Y=l.advance=()=>e[++l.index],re=()=>e.slice(l.index+1),oe=(A="",O=0)=>{l.consumed+=A,l.index+=O},xe=A=>{l.output+=A.output!=null?A.output:A.value,oe(A.value)},xr=()=>{let A=1;for(;B()==="!"&&(B(2)!=="("||B(3)==="?");)Y(),l.start++,A++;return A%2==0?!1:(l.negated=!0,l.start++,!0)},Ce=A=>{l[A]++,U.push(A)},ce=A=>{l[A]--,U.pop()},C=A=>{if(u.type==="globstar"){let O=l.braces>0&&(A.type==="comma"||A.type==="brace"),d=A.extglob===!0||x.length&&(A.type==="pipe"||A.type==="paren");A.type!=="slash"&&A.type!=="paren"&&!O&&!d&&(l.output=l.output.slice(0,-u.output.length),u.type="star",u.value="*",u.output=W,l.output+=u.output)}if(x.length&&A.type!=="paren"&&!f[A.value]&&(x[x.length-1].inner+=A.value),(A.value||A.output)&&xe(A),u&&u.type==="text"&&A.type==="text"){u.value+=A.value,u.output=(u.output||"")+A.value;return}A.prev=u,i.push(A),u=A},we=(A,O)=>{let d=F(I({},f[O]),{conditions:1,inner:""});d.prev=u,d.parens=l.parens,d.output=l.output;let S=(r.capture?"(":"")+d.open;Ce("parens"),C({type:A,value:O,output:l.output?"":_}),C({type:"paren",extglob:!0,value:Y(),output:S}),x.push(d)},Cr=A=>{let O=A.close+(r.capture?")":"");if(A.type==="negate"){let d=W;A.inner&&A.inner.length>1&&A.inner.includes("/")&&(d=g(r)),(d!==W||$()||/^\)+$/.test(re()))&&(O=A.close=`)$))${d}`),A.prev.type==="bos"&&(l.negatedExtglob=!0)}C({type:"paren",extglob:!0,value:c,output:O}),ce("parens")};if(r.fastpaths!==!1&&!/(^[*!]|[/()[\]{}"])/.test(e)){let A=!1,O=e.replace(Wn,(d,S,M,j,q,Me)=>j==="\\"?(A=!0,d):j==="?"?S?S+j+(q?L.repeat(q.length):""):Me===0?D+(q?L.repeat(q.length):""):L.repeat(M.length):j==="."?R.repeat(M.length):j==="*"?S?S+j+(q?W:""):W:S?d:`\\${d}`);return A===!0&&(r.unescape===!0?O=O.replace(/\\/g,""):O=O.replace(/\\+/g,d=>d.length%2==0?"\\\\":d?"\\":"")),O===e&&r.contains===!0?(l.output=e,l):(l.output=V.wrapOutput(O,l,t),l)}for(;!$();){if(c=Y(),c==="\0")continue;if(c==="\\"){let d=B();if(d==="/"&&r.bash!==!0||d==="."||d===";")continue;if(!d){c+="\\",C({type:"text",value:c});continue}let S=/^\\+/.exec(re()),M=0;if(S&&S[0].length>2&&(M=S[0].length,l.index+=M,M%2!=0&&(c+="\\")),r.unescape===!0?c=Y()||"":c+=Y()||"",l.brackets===0){C({type:"text",value:c});continue}}if(l.brackets>0&&(c!=="]"||u.value==="["||u.value==="[^")){if(r.posix!==!1&&c===":"){let d=u.value.slice(1);if(d.includes("[")&&(u.posix=!0,d.includes(":"))){let S=u.value.lastIndexOf("["),M=u.value.slice(0,S),j=u.value.slice(S+2),q=qn[j];if(q){u.value=M+q,l.backtrack=!0,Y(),!a.output&&i.indexOf(u)===1&&(a.output=_);continue}}}(c==="["&&B()!==":"||c==="-"&&B()==="]")&&(c=`\\${c}`),c==="]"&&(u.value==="["||u.value==="[^")&&(c=`\\${c}`),r.posix===!0&&c==="!"&&u.value==="["&&(c="^"),u.value+=c,xe({value:c});continue}if(l.quotes===1&&c!=='"'){c=V.escapeRegex(c),u.value+=c,xe({value:c});continue}if(c==='"'){l.quotes=l.quotes===1?0:1,r.keepQuotes===!0&&C({type:"text",value:c});continue}if(c==="("){Ce("parens"),C({type:"paren",value:c});continue}if(c===")"){if(l.parens===0&&r.strictBrackets===!0)throw new SyntaxError(de("opening","("));let d=x[x.length-1];if(d&&l.parens===d.parens+1){Cr(x.pop());continue}C({type:"paren",value:c,output:l.parens?")":"\\)"}),ce("parens");continue}if(c==="["){if(r.nobracket===!0||!re().includes("]")){if(r.nobracket!==!0&&r.strictBrackets===!0)throw new SyntaxError(de("closing","]"));c=`\\${c}`}else Ce("brackets");C({type:"bracket",value:c});continue}if(c==="]"){if(r.nobracket===!0||u&&u.type==="bracket"&&u.value.length===1){C({type:"text",value:c,output:`\\${c}`});continue}if(l.brackets===0){if(r.strictBrackets===!0)throw new SyntaxError(de("opening","["));C({type:"text",value:c,output:`\\${c}`});continue}ce("brackets");let d=u.value.slice(1);if(u.posix!==!0&&d[0]==="^"&&!d.includes("/")&&(c=`/${c}`),u.value+=c,xe({value:c}),r.literalBrackets===!1||V.hasRegexChars(d))continue;let S=V.escapeRegex(u.value);if(l.output=l.output.slice(0,-u.value.length),r.literalBrackets===!0){l.output+=S,u.value=S;continue}u.value=`(${o}${S}|${u.value})`,l.output+=u.value;continue}if(c==="{"&&r.nobrace!==!0){Ce("braces");let d={type:"brace",value:c,output:"(",outputIndex:l.output.length,tokensIndex:l.tokens.length};T.push(d),C(d);continue}if(c==="}"){let d=T[T.length-1];if(r.nobrace===!0||!d){C({type:"text",value:c,output:c});continue}let S=")";if(d.dots===!0){let M=i.slice(),j=[];for(let q=M.length-1;q>=0&&(i.pop(),M[q].type!=="brace");q--)M[q].type!=="dots"&&j.unshift(M[q].value);S=jn(j,r),l.backtrack=!0}if(d.comma!==!0&&d.dots!==!0){let M=l.output.slice(0,d.outputIndex),j=l.tokens.slice(d.tokensIndex);d.value=d.output="\\{",c=S="\\}",l.output=M;for(let q of j)l.output+=q.output||q.value}C({type:"brace",value:c,output:S}),ce("braces"),T.pop();continue}if(c==="|"){x.length>0&&x[x.length-1].conditions++,C({type:"text",value:c});continue}if(c===","){let d=c,S=T[T.length-1];S&&U[U.length-1]==="braces"&&(S.comma=!0,d="|"),C({type:"comma",value:c,output:d});continue}if(c==="/"){if(u.type==="dot"&&l.index===l.start+1){l.start=l.index+1,l.consumed="",l.output="",i.pop(),u=a;continue}C({type:"slash",value:c,output:v});continue}if(c==="."){if(l.braces>0&&u.type==="dot"){u.value==="."&&(u.output=R);let d=T[T.length-1];u.type="dots",u.output+=c,u.value+=c,d.dots=!0;continue}if(l.braces+l.parens===0&&u.type!=="bos"&&u.type!=="slash"){C({type:"text",value:c,output:R});continue}C({type:"dot",value:c,output:R});continue}if(c==="?"){if(!(u&&u.value==="(")&&r.noextglob!==!0&&B()==="("&&B(2)!=="?"){we("qmark",c);continue}if(u&&u.type==="paren"){let S=B(),M=c;if(S==="<"&&!V.supportsLookbehinds())throw new Error("Node.js v10 or higher is required for regex lookbehinds");(u.value==="("&&!/[!=<:]/.test(S)||S==="<"&&!/<([!=]|\w+>)/.test(re()))&&(M=`\\${c}`),C({type:"text",value:c,output:M});continue}if(r.dot!==!0&&(u.type==="slash"||u.type==="bos")){C({type:"qmark",value:c,output:k});continue}C({type:"qmark",value:c,output:L});continue}if(c==="!"){if(r.noextglob!==!0&&B()==="("&&(B(2)!=="?"||!/[!=<:]/.test(B(3)))){we("negate",c);continue}if(r.nonegate!==!0&&l.index===0){xr();continue}}if(c==="+"){if(r.noextglob!==!0&&B()==="("&&B(2)!=="?"){we("plus",c);continue}if(u&&u.value==="("||r.regex===!1){C({type:"plus",value:c,output:p});continue}if(u&&(u.type==="bracket"||u.type==="paren"||u.type==="brace")||l.parens>0){C({type:"plus",value:c});continue}C({type:"plus",value:p});continue}if(c==="@"){if(r.noextglob!==!0&&B()==="("&&B(2)!=="?"){C({type:"at",extglob:!0,value:c,output:""});continue}C({type:"text",value:c});continue}if(c!=="*"){(c==="$"||c==="^")&&(c=`\\${c}`);let d=Kn.exec(re());d&&(c+=d[0],l.index+=d[0].length),C({type:"text",value:c});continue}if(u&&(u.type==="globstar"||u.star===!0)){u.type="star",u.star=!0,u.value+=c,u.output=W,l.backtrack=!0,l.globstar=!0,oe(c);continue}let A=re();if(r.noextglob!==!0&&/^\([^?]/.test(A)){we("star",c);continue}if(u.type==="star"){if(r.noglobstar===!0){oe(c);continue}let d=u.prev,S=d.prev,M=d.type==="slash"||d.type==="bos",j=S&&(S.type==="star"||S.type==="globstar");if(r.bash===!0&&(!M||A[0]&&A[0]!=="/")){C({type:"star",value:c,output:""});continue}let q=l.braces>0&&(d.type==="comma"||d.type==="brace"),Me=x.length&&(d.type==="pipe"||d.type==="paren");if(!M&&d.type!=="paren"&&!q&&!Me){C({type:"star",value:c,output:""});continue}for(;A.slice(0,3)==="/**";){let Se=e[l.index+4];if(Se&&Se!=="/")break;A=A.slice(3),oe("/**",3)}if(d.type==="bos"&&$()){u.type="globstar",u.value+=c,u.output=g(r),l.output=u.output,l.globstar=!0,oe(c);continue}if(d.type==="slash"&&d.prev.type!=="bos"&&!j&&$()){l.output=l.output.slice(0,-(d.output+u.output).length),d.output=`(?:${d.output}`,u.type="globstar",u.output=g(r)+(r.strictSlashes?")":"|$)"),u.value+=c,l.globstar=!0,l.output+=d.output+u.output,oe(c);continue}if(d.type==="slash"&&d.prev.type!=="bos"&&A[0]==="/"){let Se=A[1]!==void 0?"|$":"";l.output=l.output.slice(0,-(d.output+u.output).length),d.output=`(?:${d.output}`,u.type="globstar",u.output=`${g(r)}${v}|${v}${Se})`,u.value+=c,l.output+=d.output+u.output,l.globstar=!0,oe(c+Y()),C({type:"slash",value:"/",output:""});continue}if(d.type==="bos"&&A[0]==="/"){u.type="globstar",u.value+=c,u.output=`(?:^|${v}|${g(r)}${v})`,l.output=u.output,l.globstar=!0,oe(c+Y()),C({type:"slash",value:"/",output:""});continue}l.output=l.output.slice(0,-u.output.length),u.type="globstar",u.output=g(r),u.value+=c,l.output+=u.output,l.globstar=!0,oe(c);continue}let O={type:"star",value:c,output:W};if(r.bash===!0){O.output=".*?",(u.type==="bos"||u.type==="slash")&&(O.output=w+O.output),C(O);continue}if(u&&(u.type==="bracket"||u.type==="paren")&&r.regex===!0){O.output=c,C(O);continue}(l.index===l.start||u.type==="slash"||u.type==="dot")&&(u.type==="dot"?(l.output+=E,u.output+=E):r.dot===!0?(l.output+=H,u.output+=H):(l.output+=w,u.output+=w),B()!=="*"&&(l.output+=_,u.output+=_)),C(O)}for(;l.brackets>0;){if(r.strictBrackets===!0)throw new SyntaxError(de("closing","]"));l.output=V.escapeLast(l.output,"["),ce("brackets")}for(;l.parens>0;){if(r.strictBrackets===!0)throw new SyntaxError(de("closing",")"));l.output=V.escapeLast(l.output,"("),ce("parens")}for(;l.braces>0;){if(r.strictBrackets===!0)throw new SyntaxError(de("closing","}"));l.output=V.escapeLast(l.output,"{"),ce("braces")}if(r.strictSlashes!==!0&&(u.type==="star"||u.type==="bracket")&&C({type:"maybe_slash",value:"",output:`${v}?`}),l.backtrack===!0){l.output="";for(let A of l.tokens)l.output+=A.output!=null?A.output:A.value,A.suffix&&(l.output+=A.suffix)}return l};nr.fastpaths=(e,t)=>{let r=I({},t),n=typeof r.maxLength=="number"?Math.min(Oe,r.maxLength):Oe,s=e.length;if(s>n)throw new SyntaxError(`Input length: ${s}, exceeds maximum allowed length: ${n}`);e=rr[e]||e;let a=V.isWindows(t),{DOT_LITERAL:i,SLASH_LITERAL:o,ONE_CHAR:h,DOTS_SLASH:m,NO_DOT:f,NO_DOTS:R,NO_DOTS_SLASH:p,STAR:v,START_ANCHOR:_}=Le.globChars(a),y=r.dot?R:f,b=r.dot?p:f,E=r.capture?"":"?:",H={negated:!1,prefix:""},L=r.bash===!0?".*?":v;r.capture&&(L=`(${L})`);let k=w=>w.noglobstar===!0?L:`(${E}(?:(?!${_}${w.dot?m:i}).)*?)`,J=w=>{switch(w){case"*":return`${y}${h}${L}`;case".*":return`${i}${h}${L}`;case"*.*":return`${y}${L}${i}${h}${L}`;case"*/*":return`${y}${L}${o}${h}${b}${L}`;case"**":return y+k(r);case"**/*":return`(?:${y}${k(r)}${o})?${b}${h}${L}`;case"**/*.*":return`(?:${y}${k(r)}${o})?${b}${L}${i}${h}${L}`;case"**/.*":return`(?:${y}${k(r)}${o})?${i}${h}${L}`;default:{let D=/^(.*?)\.(\w+)$/.exec(w);if(!D)return;let W=J(D[1]);return W?W+i+D[2]:void 0}}},ie=V.removePrefix(e,H),g=J(ie);return g&&r.strictSlashes!==!0&&(g+=`${o}?`),g};tr.exports=nr});var ir=K((ys,ar)=>{"use strict";var Fn=require("path"),Qn=er(),Ze=sr(),Ye=ye(),Xn=Re(),Zn=e=>e&&typeof e=="object"&&!Array.isArray(e),P=(e,t,r=!1)=>{if(Array.isArray(e)){let f=e.map(p=>P(p,t,r));return p=>{for(let v of f){let _=v(p);if(_)return _}return!1}}let n=Zn(e)&&e.tokens&&e.input;if(e===""||typeof e!="string"&&!n)throw new TypeError("Expected pattern to be a non-empty string");let s=t||{},a=Ye.isWindows(t),i=n?P.compileRe(e,t):P.makeRe(e,t,!1,!0),o=i.state;delete i.state;let h=()=>!1;if(s.ignore){let f=F(I({},t),{ignore:null,onMatch:null,onResult:null});h=P(s.ignore,f,r)}let m=(f,R=!1)=>{let{isMatch:p,match:v,output:_}=P.test(f,i,t,{glob:e,posix:a}),y={glob:e,state:o,regex:i,posix:a,input:f,output:_,match:v,isMatch:p};return typeof s.onResult=="function"&&s.onResult(y),p===!1?(y.isMatch=!1,R?y:!1):h(f)?(typeof s.onIgnore=="function"&&s.onIgnore(y),y.isMatch=!1,R?y:!1):(typeof s.onMatch=="function"&&s.onMatch(y),R?y:!0)};return r&&(m.state=o),m};P.test=(e,t,r,{glob:n,posix:s}={})=>{if(typeof e!="string")throw new TypeError("Expected input to be a string");if(e==="")return{isMatch:!1,output:""};let a=r||{},i=a.format||(s?Ye.toPosixSlashes:null),o=e===n,h=o&&i?i(e):e;return o===!1&&(h=i?i(e):e,o=h===n),(o===!1||a.capture===!0)&&(a.matchBase===!0||a.basename===!0?o=P.matchBase(e,t,r,s):o=t.exec(h)),{isMatch:Boolean(o),match:o,output:h}};P.matchBase=(e,t,r,n=Ye.isWindows(r))=>(t instanceof RegExp?t:P.makeRe(t,r)).test(Fn.basename(e));P.isMatch=(e,t,r)=>P(t,r)(e);P.parse=(e,t)=>Array.isArray(e)?e.map(r=>P.parse(r,t)):Ze(e,F(I({},t),{fastpaths:!1}));P.scan=(e,t)=>Qn(e,t);P.compileRe=(e,t,r=!1,n=!1)=>{if(r===!0)return e.output;let s=t||{},a=s.contains?"":"^",i=s.contains?"":"$",o=`${a}(?:${e.output})${i}`;e&&e.negated===!0&&(o=`^(?!${o}).*$`);let h=P.toRegex(o,t);return n===!0&&(h.state=e),h};P.makeRe=(e,t,r=!1,n=!1)=>{if(!e||typeof e!="string")throw new TypeError("Expected a non-empty string");let s=t||{},a={negated:!1,fastpaths:!0},i="",o;return e.startsWith("./")&&(e=e.slice(2),i=a.prefix="./"),s.fastpaths!==!1&&(e[0]==="."||e[0]==="*")&&(o=Ze.fastpaths(e,t)),o===void 0?(a=Ze(e,t),a.prefix=i+(a.prefix||"")):a.output=o,P.compileRe(a,t,r,n)};P.toRegex=(e,t)=>{try{let r=t||{};return new RegExp(e,r.flags||(r.nocase?"i":""))}catch(r){if(t&&t.debug===!0)throw r;return/$^/}};P.constants=Xn;ar.exports=P});var ur=K((_s,or)=>{"use strict";or.exports=ir()});var hr=K((bs,cr)=>{"use strict";var lr=require("util"),pr=Gt(),ae=ur(),ze=ye(),fr=e=>typeof e=="string"&&(e===""||e==="./"),N=(e,t,r)=>{t=[].concat(t),e=[].concat(e);let n=new Set,s=new Set,a=new Set,i=0,o=f=>{a.add(f.output),r&&r.onResult&&r.onResult(f)};for(let f=0;f!n.has(f));if(r&&m.length===0){if(r.failglob===!0)throw new Error(`No matches found for "${t.join(", ")}"`);if(r.nonull===!0||r.nullglob===!0)return r.unescape?t.map(f=>f.replace(/\\/g,"")):t}return m};N.match=N;N.matcher=(e,t)=>ae(e,t);N.isMatch=(e,t,r)=>ae(t,r)(e);N.any=N.isMatch;N.not=(e,t,r={})=>{t=[].concat(t).map(String);let n=new Set,s=[],a=o=>{r.onResult&&r.onResult(o),s.push(o.output)},i=N(e,t,F(I({},r),{onResult:a}));for(let o of s)i.includes(o)||n.add(o);return[...n]};N.contains=(e,t,r)=>{if(typeof e!="string")throw new TypeError(`Expected a string: "${lr.inspect(e)}"`);if(Array.isArray(t))return t.some(n=>N.contains(e,n,r));if(typeof t=="string"){if(fr(e)||fr(t))return!1;if(e.includes(t)||e.startsWith("./")&&e.slice(2).includes(t))return!0}return N.isMatch(e,t,F(I({},r),{contains:!0}))};N.matchKeys=(e,t,r)=>{if(!ze.isObject(e))throw new TypeError("Expected the first argument to be an object");let n=N(Object.keys(e),t,r),s={};for(let a of n)s[a]=e[a];return s};N.some=(e,t,r)=>{let n=[].concat(e);for(let s of[].concat(t)){let a=ae(String(s),r);if(n.some(i=>a(i)))return!0}return!1};N.every=(e,t,r)=>{let n=[].concat(e);for(let s of[].concat(t)){let a=ae(String(s),r);if(!n.every(i=>a(i)))return!1}return!0};N.all=(e,t,r)=>{if(typeof e!="string")throw new TypeError(`Expected a string: "${lr.inspect(e)}"`);return[].concat(t).every(n=>ae(n,r)(e))};N.capture=(e,t,r)=>{let n=ze.isWindows(r),a=ae.makeRe(String(e),F(I({},r),{capture:!0})).exec(n?ze.toPosixSlashes(t):t);if(a)return a.slice(1).map(i=>i===void 0?"":i)};N.makeRe=(...e)=>ae.makeRe(...e);N.scan=(...e)=>ae.scan(...e);N.parse=(e,t)=>{let r=[];for(let n of[].concat(e||[]))for(let s of pr(String(n),t))r.push(ae.parse(s,t));return r};N.braces=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");return t&&t.nobrace===!0||!/\{.*\}/.test(e)?[e]:pr(e,t)};N.braceExpand=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");return N.braces(e,F(I({},t),{expand:!0}))};cr.exports=N});var gr=K((Es,dr)=>{"use strict";dr.exports=(e,...t)=>new Promise(r=>{r(e(...t))})});var Ar=K((xs,Ve)=>{"use strict";var Yn=gr(),mr=e=>{if(e<1)throw new TypeError("Expected `concurrency` to be a number from 1 and up");let t=[],r=0,n=()=>{r--,t.length>0&&t.shift()()},s=(o,h,...m)=>{r++;let f=Yn(o,...m);h(f),f.then(n,n)},a=(o,h,...m)=>{rnew Promise(m=>a(o,m,...h));return Object.defineProperties(i,{activeCount:{get:()=>r},pendingCount:{get:()=>t.length}}),i};Ve.exports=mr;Ve.exports.default=mr});var Vn={};Or(Vn,{default:()=>es});var ve=Q(require("@yarnpkg/cli")),ne=Q(require("@yarnpkg/core")),rt=Q(require("@yarnpkg/core")),le=Q(require("clipanion")),me=class extends ve.BaseCommand{constructor(){super(...arguments);this.json=le.Option.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.production=le.Option.Boolean("--production",!1,{description:"Only install regular dependencies by omitting dev dependencies"});this.all=le.Option.Boolean("-A,--all",!1,{description:"Install the entire project"});this.workspaces=le.Option.Rest()}async execute(){let t=await ne.Configuration.find(this.context.cwd,this.context.plugins),{project:r,workspace:n}=await ne.Project.find(t,this.context.cwd),s=await ne.Cache.find(t);await r.restoreInstallState({restoreResolutions:!1});let a;if(this.all)a=new Set(r.workspaces);else if(this.workspaces.length===0){if(!n)throw new ve.WorkspaceRequiredError(r.cwd,this.context.cwd);a=new Set([n])}else a=new Set(this.workspaces.map(o=>r.getWorkspaceByIdent(rt.structUtils.parseIdent(o))));for(let o of a)for(let h of this.production?["dependencies"]:ne.Manifest.hardDependencies)for(let m of o.manifest.getForScope(h).values()){let f=r.tryWorkspaceByDescriptor(m);f!==null&&a.add(f)}for(let o of r.workspaces)a.has(o)?this.production&&o.manifest.devDependencies.clear():(o.manifest.installConfig=o.manifest.installConfig||{},o.manifest.installConfig.selfReferences=!1,o.manifest.dependencies.clear(),o.manifest.devDependencies.clear(),o.manifest.peerDependencies.clear(),o.manifest.scripts.clear());return(await ne.StreamReport.start({configuration:t,json:this.json,stdout:this.context.stdout,includeLogs:!0},async o=>{await r.install({cache:s,report:o,persistProject:!1})})).exitCode()}};me.paths=[["workspaces","focus"]],me.usage=le.Command.Usage({category:"Workspace-related commands",description:"install a single workspace and its dependencies",details:"\n This command will run an install as if the specified workspaces (and all other workspaces they depend on) were the only ones in the project. If no workspaces are explicitly listed, the active one will be assumed.\n\n Note that this command is only very moderately useful when using zero-installs, since the cache will contain all the packages anyway - meaning that the only difference between a full install and a focused install would just be a few extra lines in the `.pnp.cjs` file, at the cost of introducing an extra complexity.\n\n If the `-A,--all` flag is set, the entire project will be installed. Combine with `--production` to replicate the old `yarn install --production`.\n "});var nt=me;var Ne=Q(require("@yarnpkg/cli")),Ie=Q(require("@yarnpkg/core")),be=Q(require("@yarnpkg/core")),Z=Q(require("@yarnpkg/core")),Rr=Q(require("@yarnpkg/plugin-git")),G=Q(require("clipanion")),Be=Q(hr()),yr=Q(require("os")),_r=Q(Ar()),te=Q(require("typanion")),Ee=class extends Ne.BaseCommand{constructor(){super(...arguments);this.recursive=G.Option.Boolean("-R,--recursive",!1,{description:"Find packages via dependencies/devDependencies instead of using the workspaces field"});this.from=G.Option.Array("--from",[],{description:"An array of glob pattern idents from which to base any recursion"});this.all=G.Option.Boolean("-A,--all",!1,{description:"Run the command on all workspaces of a project"});this.verbose=G.Option.Boolean("-v,--verbose",!1,{description:"Prefix each output line with the name of the originating workspace"});this.parallel=G.Option.Boolean("-p,--parallel",!1,{description:"Run the commands in parallel"});this.interlaced=G.Option.Boolean("-i,--interlaced",!1,{description:"Print the output of commands in real-time instead of buffering it"});this.jobs=G.Option.String("-j,--jobs",{description:"The maximum number of parallel tasks that the execution will be limited to; or `unlimited`",validator:te.isOneOf([te.isEnum(["unlimited"]),te.applyCascade(te.isNumber(),[te.isInteger(),te.isAtLeast(1)])])});this.topological=G.Option.Boolean("-t,--topological",!1,{description:"Run the command after all workspaces it depends on (regular) have finished"});this.topologicalDev=G.Option.Boolean("--topological-dev",!1,{description:"Run the command after all workspaces it depends on (regular + dev) have finished"});this.include=G.Option.Array("--include",[],{description:"An array of glob pattern idents; only matching workspaces will be traversed"});this.exclude=G.Option.Array("--exclude",[],{description:"An array of glob pattern idents; matching workspaces won't be traversed"});this.publicOnly=G.Option.Boolean("--no-private",{description:"Avoid running the command on private workspaces"});this.since=G.Option.String("--since",{description:"Only include workspaces that have been changed since the specified ref.",tolerateBoolean:!0});this.commandName=G.Option.String();this.args=G.Option.Proxy()}async execute(){let t=await Ie.Configuration.find(this.context.cwd,this.context.plugins),{project:r,workspace:n}=await Ie.Project.find(t,this.context.cwd);if(!this.all&&!n)throw new Ne.WorkspaceRequiredError(r.cwd,this.context.cwd);let s=this.cli.process([this.commandName,...this.args]),a=s.path.length===1&&s.path[0]==="run"&&typeof s.scriptName!="undefined"?s.scriptName:null;if(s.path.length===0)throw new G.UsageError("Invalid subcommand name for iteration - use the 'run' keyword if you wish to execute a script");let i=this.all?r.topLevelWorkspace:n,o=this.since?Array.from(await Rr.gitUtils.fetchChangedWorkspaces({ref:this.since,project:r})):[i,...this.from.length>0?i.getRecursiveWorkspaceChildren():[]],h=g=>Be.default.isMatch(Z.structUtils.stringifyIdent(g.locator),this.from),m=this.from.length>0?o.filter(h):o,f=new Set([...m,...m.map(g=>[...this.recursive?this.since?g.getRecursiveWorkspaceDependents():g.getRecursiveWorkspaceDependencies():g.getRecursiveWorkspaceChildren()]).flat()]),R=[],p=!1;if(a==null?void 0:a.includes(":")){for(let g of r.workspaces)if(g.manifest.scripts.has(a)&&(p=!p,p===!1))break}for(let g of f)a&&!g.manifest.scripts.has(a)&&!p||a===process.env.npm_lifecycle_event&&g.cwd===n.cwd||this.include.length>0&&!Be.default.isMatch(Z.structUtils.stringifyIdent(g.locator),this.include)||this.exclude.length>0&&Be.default.isMatch(Z.structUtils.stringifyIdent(g.locator),this.exclude)||this.publicOnly&&g.manifest.private===!0||R.push(g);let v=this.parallel?this.jobs==="unlimited"?Infinity:this.jobs||Math.max(1,(0,yr.cpus)().length/2):1,_=v===1?!1:this.parallel,y=_?this.interlaced:!0,b=(0,_r.default)(v),E=new Map,H=new Set,L=0,k=null,J=!1,ie=await be.StreamReport.start({configuration:t,stdout:this.context.stdout},async g=>{let w=async(D,{commandIndex:W})=>{if(J)return-1;!_&&this.verbose&&W>1&&g.reportSeparator();let l=zn(D,{configuration:t,verbose:this.verbose,commandIndex:W}),[x,T]=br(g,{prefix:l,interlaced:y}),[U,u]=br(g,{prefix:l,interlaced:y});try{this.verbose&&g.reportInfo(null,`${l} Process started`);let c=Date.now(),$=await this.cli.run([this.commandName,...this.args],{cwd:D.cwd,stdout:x,stderr:U})||0;x.end(),U.end(),await T,await u;let B=Date.now();if(this.verbose){let Y=t.get("enableTimers")?`, completed in ${Z.formatUtils.pretty(t,B-c,Z.formatUtils.Type.DURATION)}`:"";g.reportInfo(null,`${l} Process exited (exit code ${$})${Y}`)}return $===130&&(J=!0,k=$),$}catch(c){throw x.end(),U.end(),await T,await u,c}};for(let D of R)E.set(D.anchoredLocator.locatorHash,D);for(;E.size>0&&!g.hasErrors();){let D=[];for(let[x,T]of E){if(H.has(T.anchoredDescriptor.descriptorHash))continue;let U=!0;if(this.topological||this.topologicalDev){let u=this.topologicalDev?new Map([...T.manifest.dependencies,...T.manifest.devDependencies]):T.manifest.dependencies;for(let c of u.values()){let $=r.tryWorkspaceByDescriptor(c);if(U=$===null||!E.has($.anchoredLocator.locatorHash),!U)break}}if(!!U&&(H.add(T.anchoredDescriptor.descriptorHash),D.push(b(async()=>{let u=await w(T,{commandIndex:++L});return E.delete(x),H.delete(T.anchoredDescriptor.descriptorHash),u})),!_))break}if(D.length===0){let x=Array.from(E.values()).map(T=>Z.structUtils.prettyLocator(t,T.anchoredLocator)).join(", ");g.reportError(be.MessageName.CYCLIC_DEPENDENCIES,`Dependency cycle detected (${x})`);return}let l=(await Promise.all(D)).find(x=>x!==0);k===null&&(k=typeof l!="undefined"?1:k),(this.topological||this.topologicalDev)&&typeof l!="undefined"&&g.reportError(be.MessageName.UNNAMED,"The command failed for workspaces that are depended upon by other workspaces; can't satisfy the dependency graph")}});return k!==null?k:ie.exitCode()}};Ee.paths=[["workspaces","foreach"]],Ee.usage=G.Command.Usage({category:"Workspace-related commands",description:"run a command on all workspaces",details:"\n This command will run a given sub-command on current and all its descendant workspaces. Various flags can alter the exact behavior of the command:\n\n - If `-p,--parallel` is set, the commands will be ran in parallel; they'll by default be limited to a number of parallel tasks roughly equal to half your core number, but that can be overridden via `-j,--jobs`, or disabled by setting `-j unlimited`.\n\n - If `-p,--parallel` and `-i,--interlaced` are both set, Yarn will print the lines from the output as it receives them. If `-i,--interlaced` wasn't set, it would instead buffer the output from each process and print the resulting buffers only after their source processes have exited.\n\n - If `-t,--topological` is set, Yarn will only run the command after all workspaces that it depends on through the `dependencies` field have successfully finished executing. If `--topological-dev` is set, both the `dependencies` and `devDependencies` fields will be considered when figuring out the wait points.\n\n - If `-A,--all` is set, Yarn will run the command on all the workspaces of a project. By default yarn runs the command only on current and all its descendant workspaces.\n\n - If `-R,--recursive` is set, Yarn will find workspaces to run the command on by recursively evaluating `dependencies` and `devDependencies` fields, instead of looking at the `workspaces` fields.\n\n - If `--from` is set, Yarn will use the packages matching the 'from' glob as the starting point for any recursive search.\n\n - If `--since` is set, Yarn will only run the command on workspaces that have been modified since the specified ref. By default Yarn will use the refs specified by the `changesetBaseRefs` configuration option.\n\n - The command may apply to only some workspaces through the use of `--include` which acts as a whitelist. The `--exclude` flag will do the opposite and will be a list of packages that mustn't execute the script. Both flags accept glob patterns (if valid Idents and supported by [micromatch](https://github.com/micromatch/micromatch)). Make sure to escape the patterns, to prevent your own shell from trying to expand them.\n\n Adding the `-v,--verbose` flag will cause Yarn to print more information; in particular the name of the workspace that generated the output will be printed at the front of each line.\n\n If the command is `run` and the script being run does not exist the child workspace will be skipped without error.\n ",examples:[["Publish current and all descendant packages","yarn workspaces foreach npm publish --tolerate-republish"],["Run build script on current and all descendant packages","yarn workspaces foreach run build"],["Run build script on current and all descendant packages in parallel, building package dependencies first","yarn workspaces foreach -pt run build"],["Run build script on several packages and all their dependencies, building dependencies first","yarn workspaces foreach -ptR --from '{workspace-a,workspace-b}' run build"]]});var Er=Ee;function br(e,{prefix:t,interlaced:r}){let n=e.createStreamReporter(t),s=new Z.miscUtils.DefaultStream;s.pipe(n,{end:!1}),s.on("finish",()=>{n.end()});let a=new Promise(o=>{n.on("finish",()=>{o(s.active)})});if(r)return[s,a];let i=new Z.miscUtils.BufferStream;return i.pipe(s,{end:!1}),i.on("finish",()=>{s.end()}),[i,a]}function zn(e,{configuration:t,commandIndex:r,verbose:n}){if(!n)return null;let s=Z.structUtils.convertToIdent(e.locator),i=`[${Z.structUtils.stringifyIdent(s)}]:`,o=["#2E86AB","#A23B72","#F18F01","#C73E1D","#CCE2A3"],h=o[r%o.length];return Z.formatUtils.pretty(t,i,h)}var Jn={commands:[nt,Er]},es=Jn;return Vn;})(); /*! * fill-range * * Copyright (c) 2014-present, Jon Schlinkert. * Licensed under the MIT License. */ /*! * is-number * * Copyright (c) 2014-present, Jon Schlinkert. * Released under the MIT License. */ /*! * to-regex-range * * Copyright (c) 2015-present, Jon Schlinkert. * Released under the MIT License. */ return plugin; } }; ================================================ FILE: .yarn/releases/yarn-1.22.17.cjs ================================================ #!/usr/bin/env node module.exports = /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // identity function for calling harmony imports with the correct context /******/ __webpack_require__.i = function(value) { return value; }; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 549); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports) { module.exports = require("path"); /***/ }), /* 1 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (immutable) */ __webpack_exports__["a"] = __extends; /* unused harmony export __assign */ /* unused harmony export __rest */ /* unused harmony export __decorate */ /* unused harmony export __param */ /* unused harmony export __metadata */ /* unused harmony export __awaiter */ /* unused harmony export __generator */ /* unused harmony export __exportStar */ /* unused harmony export __values */ /* unused harmony export __read */ /* unused harmony export __spread */ /* unused harmony export __await */ /* unused harmony export __asyncGenerator */ /* unused harmony export __asyncDelegator */ /* unused harmony export __asyncValues */ /* unused harmony export __makeTemplateObject */ /* unused harmony export __importStar */ /* unused harmony export __importDefault */ /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; } return __assign.apply(this, arguments); } function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) t[p[i]] = s[p[i]]; return t; } function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function __param(paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } } function __metadata(metadataKey, metadataValue) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); } function __awaiter(thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } function __exportStar(m, exports) { for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; } function __values(o) { var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; if (m) return m.call(o); return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } function __await(v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } function __asyncGenerator(thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } } function __asyncDelegator(o) { var i, p; return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } } function __asyncValues(o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } } function __makeTemplateObject(cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; }; function __importStar(mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result.default = mod; return result; } function __importDefault(mod) { return (mod && mod.__esModule) ? mod : { default: mod }; } /***/ }), /* 2 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; var _promise = __webpack_require__(227); var _promise2 = _interopRequireDefault(_promise); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } exports.default = function (fn) { return function () { var gen = fn.apply(this, arguments); return new _promise2.default(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return _promise2.default.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }; /***/ }), /* 3 */ /***/ (function(module, exports) { module.exports = require("util"); /***/ }), /* 4 */ /***/ (function(module, exports) { module.exports = require("fs"); /***/ }), /* 5 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getFirstSuitableFolder = exports.readFirstAvailableStream = exports.makeTempDir = exports.hardlinksWork = exports.writeFilePreservingEol = exports.getFileSizeOnDisk = exports.walk = exports.symlink = exports.find = exports.readJsonAndFile = exports.readJson = exports.readFileAny = exports.hardlinkBulk = exports.copyBulk = exports.unlink = exports.glob = exports.link = exports.chmod = exports.lstat = exports.exists = exports.mkdirp = exports.stat = exports.access = exports.rename = exports.readdir = exports.realpath = exports.readlink = exports.writeFile = exports.open = exports.readFileBuffer = exports.lockQueue = exports.constants = undefined; var _asyncToGenerator2; function _load_asyncToGenerator() { return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(2)); } let buildActionsForCopy = (() => { var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { // let build = (() => { var _ref5 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { const src = data.src, dest = data.dest, type = data.type; const onFresh = data.onFresh || noop; const onDone = data.onDone || noop; // TODO https://github.com/yarnpkg/yarn/issues/3751 // related to bundled dependencies handling if (files.has(dest.toLowerCase())) { reporter.verbose(`The case-insensitive file ${dest} shouldn't be copied twice in one bulk copy`); } else { files.add(dest.toLowerCase()); } if (type === 'symlink') { yield mkdirp((_path || _load_path()).default.dirname(dest)); onFresh(); actions.symlink.push({ dest, linkname: src }); onDone(); return; } if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { // ignored file return; } const srcStat = yield lstat(src); let srcFiles; if (srcStat.isDirectory()) { srcFiles = yield readdir(src); } let destStat; try { // try accessing the destination destStat = yield lstat(dest); } catch (e) { // proceed if destination doesn't exist, otherwise error if (e.code !== 'ENOENT') { throw e; } } // if destination exists if (destStat) { const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); const bothFiles = srcStat.isFile() && destStat.isFile(); // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving // us modes that aren't valid. investigate this, it's generally safe to proceed. /* if (srcStat.mode !== destStat.mode) { try { await access(dest, srcStat.mode); } catch (err) {} } */ if (bothFiles && artifactFiles.has(dest)) { // this file gets changed during build, likely by a custom install script. Don't bother checking it. onDone(); reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); return; } if (bothFiles && srcStat.size === destStat.size && (0, (_fsNormalized || _load_fsNormalized()).fileDatesEqual)(srcStat.mtime, destStat.mtime)) { // we can safely assume this is the same file onDone(); reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.size, +srcStat.mtime)); return; } if (bothSymlinks) { const srcReallink = yield readlink(src); if (srcReallink === (yield readlink(dest))) { // if both symlinks are the same then we can continue on onDone(); reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); return; } } if (bothFolders) { // mark files that aren't in this folder as possibly extraneous const destFiles = yield readdir(dest); invariant(srcFiles, 'src files not initialised'); for (var _iterator4 = destFiles, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { var _ref6; if (_isArray4) { if (_i4 >= _iterator4.length) break; _ref6 = _iterator4[_i4++]; } else { _i4 = _iterator4.next(); if (_i4.done) break; _ref6 = _i4.value; } const file = _ref6; if (srcFiles.indexOf(file) < 0) { const loc = (_path || _load_path()).default.join(dest, file); possibleExtraneous.add(loc); if ((yield lstat(loc)).isDirectory()) { for (var _iterator5 = yield readdir(loc), _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { var _ref7; if (_isArray5) { if (_i5 >= _iterator5.length) break; _ref7 = _iterator5[_i5++]; } else { _i5 = _iterator5.next(); if (_i5.done) break; _ref7 = _i5.value; } const file = _ref7; possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); } } } } } } if (destStat && destStat.isSymbolicLink()) { yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); destStat = null; } if (srcStat.isSymbolicLink()) { onFresh(); const linkname = yield readlink(src); actions.symlink.push({ dest, linkname }); onDone(); } else if (srcStat.isDirectory()) { if (!destStat) { reporter.verbose(reporter.lang('verboseFileFolder', dest)); yield mkdirp(dest); } const destParts = dest.split((_path || _load_path()).default.sep); while (destParts.length) { files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); destParts.pop(); } // push all files to queue invariant(srcFiles, 'src files not initialised'); let remaining = srcFiles.length; if (!remaining) { onDone(); } for (var _iterator6 = srcFiles, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { var _ref8; if (_isArray6) { if (_i6 >= _iterator6.length) break; _ref8 = _iterator6[_i6++]; } else { _i6 = _iterator6.next(); if (_i6.done) break; _ref8 = _i6.value; } const file = _ref8; queue.push({ dest: (_path || _load_path()).default.join(dest, file), onFresh, onDone: function (_onDone) { function onDone() { return _onDone.apply(this, arguments); } onDone.toString = function () { return _onDone.toString(); }; return onDone; }(function () { if (--remaining === 0) { onDone(); } }), src: (_path || _load_path()).default.join(src, file) }); } } else if (srcStat.isFile()) { onFresh(); actions.file.push({ src, dest, atime: srcStat.atime, mtime: srcStat.mtime, mode: srcStat.mode }); onDone(); } else { throw new Error(`unsure how to copy this: ${src}`); } }); return function build(_x5) { return _ref5.apply(this, arguments); }; })(); const artifactFiles = new Set(events.artifactFiles || []); const files = new Set(); // initialise events for (var _iterator = queue, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var _ref2; if (_isArray) { if (_i >= _iterator.length) break; _ref2 = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) break; _ref2 = _i.value; } const item = _ref2; const onDone = item.onDone; item.onDone = function () { events.onProgress(item.dest); if (onDone) { onDone(); } }; } events.onStart(queue.length); // start building actions const actions = { file: [], symlink: [], link: [] }; // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items // at a time due to the requirement to push items onto the queue while (queue.length) { const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); yield Promise.all(items.map(build)); } // simulate the existence of some files to prevent considering them extraneous for (var _iterator2 = artifactFiles, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { var _ref3; if (_isArray2) { if (_i2 >= _iterator2.length) break; _ref3 = _iterator2[_i2++]; } else { _i2 = _iterator2.next(); if (_i2.done) break; _ref3 = _i2.value; } const file = _ref3; if (possibleExtraneous.has(file)) { reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); possibleExtraneous.delete(file); } } for (var _iterator3 = possibleExtraneous, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { var _ref4; if (_isArray3) { if (_i3 >= _iterator3.length) break; _ref4 = _iterator3[_i3++]; } else { _i3 = _iterator3.next(); if (_i3.done) break; _ref4 = _i3.value; } const loc = _ref4; if (files.has(loc.toLowerCase())) { possibleExtraneous.delete(loc); } } return actions; }); return function buildActionsForCopy(_x, _x2, _x3, _x4) { return _ref.apply(this, arguments); }; })(); let buildActionsForHardlink = (() => { var _ref9 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { // let build = (() => { var _ref13 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { const src = data.src, dest = data.dest; const onFresh = data.onFresh || noop; const onDone = data.onDone || noop; if (files.has(dest.toLowerCase())) { // Fixes issue https://github.com/yarnpkg/yarn/issues/2734 // When bulk hardlinking we have A -> B structure that we want to hardlink to A1 -> B1, // package-linker passes that modules A1 and B1 need to be hardlinked, // the recursive linking algorithm of A1 ends up scheduling files in B1 to be linked twice which will case // an exception. onDone(); return; } files.add(dest.toLowerCase()); if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { // ignored file return; } const srcStat = yield lstat(src); let srcFiles; if (srcStat.isDirectory()) { srcFiles = yield readdir(src); } const destExists = yield exists(dest); if (destExists) { const destStat = yield lstat(dest); const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); const bothFiles = srcStat.isFile() && destStat.isFile(); if (srcStat.mode !== destStat.mode) { try { yield access(dest, srcStat.mode); } catch (err) { // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving // us modes that aren't valid. investigate this, it's generally safe to proceed. reporter.verbose(err); } } if (bothFiles && artifactFiles.has(dest)) { // this file gets changed during build, likely by a custom install script. Don't bother checking it. onDone(); reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); return; } // correct hardlink if (bothFiles && srcStat.ino !== null && srcStat.ino === destStat.ino) { onDone(); reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.ino)); return; } if (bothSymlinks) { const srcReallink = yield readlink(src); if (srcReallink === (yield readlink(dest))) { // if both symlinks are the same then we can continue on onDone(); reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); return; } } if (bothFolders) { // mark files that aren't in this folder as possibly extraneous const destFiles = yield readdir(dest); invariant(srcFiles, 'src files not initialised'); for (var _iterator10 = destFiles, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { var _ref14; if (_isArray10) { if (_i10 >= _iterator10.length) break; _ref14 = _iterator10[_i10++]; } else { _i10 = _iterator10.next(); if (_i10.done) break; _ref14 = _i10.value; } const file = _ref14; if (srcFiles.indexOf(file) < 0) { const loc = (_path || _load_path()).default.join(dest, file); possibleExtraneous.add(loc); if ((yield lstat(loc)).isDirectory()) { for (var _iterator11 = yield readdir(loc), _isArray11 = Array.isArray(_iterator11), _i11 = 0, _iterator11 = _isArray11 ? _iterator11 : _iterator11[Symbol.iterator]();;) { var _ref15; if (_isArray11) { if (_i11 >= _iterator11.length) break; _ref15 = _iterator11[_i11++]; } else { _i11 = _iterator11.next(); if (_i11.done) break; _ref15 = _i11.value; } const file = _ref15; possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); } } } } } } if (srcStat.isSymbolicLink()) { onFresh(); const linkname = yield readlink(src); actions.symlink.push({ dest, linkname }); onDone(); } else if (srcStat.isDirectory()) { reporter.verbose(reporter.lang('verboseFileFolder', dest)); yield mkdirp(dest); const destParts = dest.split((_path || _load_path()).default.sep); while (destParts.length) { files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); destParts.pop(); } // push all files to queue invariant(srcFiles, 'src files not initialised'); let remaining = srcFiles.length; if (!remaining) { onDone(); } for (var _iterator12 = srcFiles, _isArray12 = Array.isArray(_iterator12), _i12 = 0, _iterator12 = _isArray12 ? _iterator12 : _iterator12[Symbol.iterator]();;) { var _ref16; if (_isArray12) { if (_i12 >= _iterator12.length) break; _ref16 = _iterator12[_i12++]; } else { _i12 = _iterator12.next(); if (_i12.done) break; _ref16 = _i12.value; } const file = _ref16; queue.push({ onFresh, src: (_path || _load_path()).default.join(src, file), dest: (_path || _load_path()).default.join(dest, file), onDone: function (_onDone2) { function onDone() { return _onDone2.apply(this, arguments); } onDone.toString = function () { return _onDone2.toString(); }; return onDone; }(function () { if (--remaining === 0) { onDone(); } }) }); } } else if (srcStat.isFile()) { onFresh(); actions.link.push({ src, dest, removeDest: destExists }); onDone(); } else { throw new Error(`unsure how to copy this: ${src}`); } }); return function build(_x10) { return _ref13.apply(this, arguments); }; })(); const artifactFiles = new Set(events.artifactFiles || []); const files = new Set(); // initialise events for (var _iterator7 = queue, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { var _ref10; if (_isArray7) { if (_i7 >= _iterator7.length) break; _ref10 = _iterator7[_i7++]; } else { _i7 = _iterator7.next(); if (_i7.done) break; _ref10 = _i7.value; } const item = _ref10; const onDone = item.onDone || noop; item.onDone = function () { events.onProgress(item.dest); onDone(); }; } events.onStart(queue.length); // start building actions const actions = { file: [], symlink: [], link: [] }; // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items // at a time due to the requirement to push items onto the queue while (queue.length) { const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); yield Promise.all(items.map(build)); } // simulate the existence of some files to prevent considering them extraneous for (var _iterator8 = artifactFiles, _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { var _ref11; if (_isArray8) { if (_i8 >= _iterator8.length) break; _ref11 = _iterator8[_i8++]; } else { _i8 = _iterator8.next(); if (_i8.done) break; _ref11 = _i8.value; } const file = _ref11; if (possibleExtraneous.has(file)) { reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); possibleExtraneous.delete(file); } } for (var _iterator9 = possibleExtraneous, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { var _ref12; if (_isArray9) { if (_i9 >= _iterator9.length) break; _ref12 = _iterator9[_i9++]; } else { _i9 = _iterator9.next(); if (_i9.done) break; _ref12 = _i9.value; } const loc = _ref12; if (files.has(loc.toLowerCase())) { possibleExtraneous.delete(loc); } } return actions; }); return function buildActionsForHardlink(_x6, _x7, _x8, _x9) { return _ref9.apply(this, arguments); }; })(); let copyBulk = exports.copyBulk = (() => { var _ref17 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { const events = { onStart: _events && _events.onStart || noop, onProgress: _events && _events.onProgress || noop, possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), ignoreBasenames: _events && _events.ignoreBasenames || [], artifactFiles: _events && _events.artifactFiles || [] }; const actions = yield buildActionsForCopy(queue, events, events.possibleExtraneous, reporter); events.onStart(actions.file.length + actions.symlink.length + actions.link.length); const fileActions = actions.file; const currentlyWriting = new Map(); yield (_promise || _load_promise()).queue(fileActions, (() => { var _ref18 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { let writePromise; while (writePromise = currentlyWriting.get(data.dest)) { yield writePromise; } reporter.verbose(reporter.lang('verboseFileCopy', data.src, data.dest)); const copier = (0, (_fsNormalized || _load_fsNormalized()).copyFile)(data, function () { return currentlyWriting.delete(data.dest); }); currentlyWriting.set(data.dest, copier); events.onProgress(data.dest); return copier; }); return function (_x14) { return _ref18.apply(this, arguments); }; })(), CONCURRENT_QUEUE_ITEMS); // we need to copy symlinks last as they could reference files we were copying const symlinkActions = actions.symlink; yield (_promise || _load_promise()).queue(symlinkActions, function (data) { const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); return symlink(linkname, data.dest); }); }); return function copyBulk(_x11, _x12, _x13) { return _ref17.apply(this, arguments); }; })(); let hardlinkBulk = exports.hardlinkBulk = (() => { var _ref19 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { const events = { onStart: _events && _events.onStart || noop, onProgress: _events && _events.onProgress || noop, possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), artifactFiles: _events && _events.artifactFiles || [], ignoreBasenames: [] }; const actions = yield buildActionsForHardlink(queue, events, events.possibleExtraneous, reporter); events.onStart(actions.file.length + actions.symlink.length + actions.link.length); const fileActions = actions.link; yield (_promise || _load_promise()).queue(fileActions, (() => { var _ref20 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { reporter.verbose(reporter.lang('verboseFileLink', data.src, data.dest)); if (data.removeDest) { yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(data.dest); } yield link(data.src, data.dest); }); return function (_x18) { return _ref20.apply(this, arguments); }; })(), CONCURRENT_QUEUE_ITEMS); // we need to copy symlinks last as they could reference files we were copying const symlinkActions = actions.symlink; yield (_promise || _load_promise()).queue(symlinkActions, function (data) { const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); return symlink(linkname, data.dest); }); }); return function hardlinkBulk(_x15, _x16, _x17) { return _ref19.apply(this, arguments); }; })(); let readFileAny = exports.readFileAny = (() => { var _ref21 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (files) { for (var _iterator13 = files, _isArray13 = Array.isArray(_iterator13), _i13 = 0, _iterator13 = _isArray13 ? _iterator13 : _iterator13[Symbol.iterator]();;) { var _ref22; if (_isArray13) { if (_i13 >= _iterator13.length) break; _ref22 = _iterator13[_i13++]; } else { _i13 = _iterator13.next(); if (_i13.done) break; _ref22 = _i13.value; } const file = _ref22; if (yield exists(file)) { return readFile(file); } } return null; }); return function readFileAny(_x19) { return _ref21.apply(this, arguments); }; })(); let readJson = exports.readJson = (() => { var _ref23 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { return (yield readJsonAndFile(loc)).object; }); return function readJson(_x20) { return _ref23.apply(this, arguments); }; })(); let readJsonAndFile = exports.readJsonAndFile = (() => { var _ref24 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { const file = yield readFile(loc); try { return { object: (0, (_map || _load_map()).default)(JSON.parse(stripBOM(file))), content: file }; } catch (err) { err.message = `${loc}: ${err.message}`; throw err; } }); return function readJsonAndFile(_x21) { return _ref24.apply(this, arguments); }; })(); let find = exports.find = (() => { var _ref25 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (filename, dir) { const parts = dir.split((_path || _load_path()).default.sep); while (parts.length) { const loc = parts.concat(filename).join((_path || _load_path()).default.sep); if (yield exists(loc)) { return loc; } else { parts.pop(); } } return false; }); return function find(_x22, _x23) { return _ref25.apply(this, arguments); }; })(); let symlink = exports.symlink = (() => { var _ref26 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (src, dest) { if (process.platform !== 'win32') { // use relative paths otherwise which will be retained if the directory is moved src = (_path || _load_path()).default.relative((_path || _load_path()).default.dirname(dest), src); // When path.relative returns an empty string for the current directory, we should instead use // '.', which is a valid fs.symlink target. src = src || '.'; } try { const stats = yield lstat(dest); if (stats.isSymbolicLink()) { const resolved = dest; if (resolved === src) { return; } } } catch (err) { if (err.code !== 'ENOENT') { throw err; } } // We use rimraf for unlink which never throws an ENOENT on missing target yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); if (process.platform === 'win32') { // use directory junctions if possible on win32, this requires absolute paths yield fsSymlink(src, dest, 'junction'); } else { yield fsSymlink(src, dest); } }); return function symlink(_x24, _x25) { return _ref26.apply(this, arguments); }; })(); let walk = exports.walk = (() => { var _ref27 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir, relativeDir, ignoreBasenames = new Set()) { let files = []; let filenames = yield readdir(dir); if (ignoreBasenames.size) { filenames = filenames.filter(function (name) { return !ignoreBasenames.has(name); }); } for (var _iterator14 = filenames, _isArray14 = Array.isArray(_iterator14), _i14 = 0, _iterator14 = _isArray14 ? _iterator14 : _iterator14[Symbol.iterator]();;) { var _ref28; if (_isArray14) { if (_i14 >= _iterator14.length) break; _ref28 = _iterator14[_i14++]; } else { _i14 = _iterator14.next(); if (_i14.done) break; _ref28 = _i14.value; } const name = _ref28; const relative = relativeDir ? (_path || _load_path()).default.join(relativeDir, name) : name; const loc = (_path || _load_path()).default.join(dir, name); const stat = yield lstat(loc); files.push({ relative, basename: name, absolute: loc, mtime: +stat.mtime }); if (stat.isDirectory()) { files = files.concat((yield walk(loc, relative, ignoreBasenames))); } } return files; }); return function walk(_x26, _x27) { return _ref27.apply(this, arguments); }; })(); let getFileSizeOnDisk = exports.getFileSizeOnDisk = (() => { var _ref29 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { const stat = yield lstat(loc); const size = stat.size, blockSize = stat.blksize; return Math.ceil(size / blockSize) * blockSize; }); return function getFileSizeOnDisk(_x28) { return _ref29.apply(this, arguments); }; })(); let getEolFromFile = (() => { var _ref30 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path) { if (!(yield exists(path))) { return undefined; } const buffer = yield readFileBuffer(path); for (let i = 0; i < buffer.length; ++i) { if (buffer[i] === cr) { return '\r\n'; } if (buffer[i] === lf) { return '\n'; } } return undefined; }); return function getEolFromFile(_x29) { return _ref30.apply(this, arguments); }; })(); let writeFilePreservingEol = exports.writeFilePreservingEol = (() => { var _ref31 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path, data) { const eol = (yield getEolFromFile(path)) || (_os || _load_os()).default.EOL; if (eol !== '\n') { data = data.replace(/\n/g, eol); } yield writeFile(path, data); }); return function writeFilePreservingEol(_x30, _x31) { return _ref31.apply(this, arguments); }; })(); let hardlinksWork = exports.hardlinksWork = (() => { var _ref32 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir) { const filename = 'test-file' + Math.random(); const file = (_path || _load_path()).default.join(dir, filename); const fileLink = (_path || _load_path()).default.join(dir, filename + '-link'); try { yield writeFile(file, 'test'); yield link(file, fileLink); } catch (err) { return false; } finally { yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(file); yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(fileLink); } return true; }); return function hardlinksWork(_x32) { return _ref32.apply(this, arguments); }; })(); // not a strict polyfill for Node's fs.mkdtemp let makeTempDir = exports.makeTempDir = (() => { var _ref33 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (prefix) { const dir = (_path || _load_path()).default.join((_os || _load_os()).default.tmpdir(), `yarn-${prefix || ''}-${Date.now()}-${Math.random()}`); yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dir); yield mkdirp(dir); return dir; }); return function makeTempDir(_x33) { return _ref33.apply(this, arguments); }; })(); let readFirstAvailableStream = exports.readFirstAvailableStream = (() => { var _ref34 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths) { for (var _iterator15 = paths, _isArray15 = Array.isArray(_iterator15), _i15 = 0, _iterator15 = _isArray15 ? _iterator15 : _iterator15[Symbol.iterator]();;) { var _ref35; if (_isArray15) { if (_i15 >= _iterator15.length) break; _ref35 = _iterator15[_i15++]; } else { _i15 = _iterator15.next(); if (_i15.done) break; _ref35 = _i15.value; } const path = _ref35; try { const fd = yield open(path, 'r'); return (_fs || _load_fs()).default.createReadStream(path, { fd }); } catch (err) { // Try the next one } } return null; }); return function readFirstAvailableStream(_x34) { return _ref34.apply(this, arguments); }; })(); let getFirstSuitableFolder = exports.getFirstSuitableFolder = (() => { var _ref36 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths, mode = constants.W_OK | constants.X_OK) { const result = { skipped: [], folder: null }; for (var _iterator16 = paths, _isArray16 = Array.isArray(_iterator16), _i16 = 0, _iterator16 = _isArray16 ? _iterator16 : _iterator16[Symbol.iterator]();;) { var _ref37; if (_isArray16) { if (_i16 >= _iterator16.length) break; _ref37 = _iterator16[_i16++]; } else { _i16 = _iterator16.next(); if (_i16.done) break; _ref37 = _i16.value; } const folder = _ref37; try { yield mkdirp(folder); yield access(folder, mode); result.folder = folder; return result; } catch (error) { result.skipped.push({ error, folder }); } } return result; }); return function getFirstSuitableFolder(_x35) { return _ref36.apply(this, arguments); }; })(); exports.copy = copy; exports.readFile = readFile; exports.readFileRaw = readFileRaw; exports.normalizeOS = normalizeOS; var _fs; function _load_fs() { return _fs = _interopRequireDefault(__webpack_require__(4)); } var _glob; function _load_glob() { return _glob = _interopRequireDefault(__webpack_require__(99)); } var _os; function _load_os() { return _os = _interopRequireDefault(__webpack_require__(46)); } var _path; function _load_path() { return _path = _interopRequireDefault(__webpack_require__(0)); } var _blockingQueue; function _load_blockingQueue() { return _blockingQueue = _interopRequireDefault(__webpack_require__(111)); } var _promise; function _load_promise() { return _promise = _interopRequireWildcard(__webpack_require__(51)); } var _promise2; function _load_promise2() { return _promise2 = __webpack_require__(51); } var _map; function _load_map() { return _map = _interopRequireDefault(__webpack_require__(29)); } var _fsNormalized; function _load_fsNormalized() { return _fsNormalized = __webpack_require__(219); } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const constants = exports.constants = typeof (_fs || _load_fs()).default.constants !== 'undefined' ? (_fs || _load_fs()).default.constants : { R_OK: (_fs || _load_fs()).default.R_OK, W_OK: (_fs || _load_fs()).default.W_OK, X_OK: (_fs || _load_fs()).default.X_OK }; const lockQueue = exports.lockQueue = new (_blockingQueue || _load_blockingQueue()).default('fs lock'); const readFileBuffer = exports.readFileBuffer = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readFile); const open = exports.open = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.open); const writeFile = exports.writeFile = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.writeFile); const readlink = exports.readlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readlink); const realpath = exports.realpath = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.realpath); const readdir = exports.readdir = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readdir); const rename = exports.rename = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.rename); const access = exports.access = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.access); const stat = exports.stat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.stat); const mkdirp = exports.mkdirp = (0, (_promise2 || _load_promise2()).promisify)(__webpack_require__(145)); const exists = exports.exists = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.exists, true); const lstat = exports.lstat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.lstat); const chmod = exports.chmod = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.chmod); const link = exports.link = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.link); const glob = exports.glob = (0, (_promise2 || _load_promise2()).promisify)((_glob || _load_glob()).default); exports.unlink = (_fsNormalized || _load_fsNormalized()).unlink; // fs.copyFile uses the native file copying instructions on the system, performing much better // than any JS-based solution and consumes fewer resources. Repeated testing to fine tune the // concurrency level revealed 128 as the sweet spot on a quad-core, 16 CPU Intel system with SSD. const CONCURRENT_QUEUE_ITEMS = (_fs || _load_fs()).default.copyFile ? 128 : 4; const fsSymlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.symlink); const invariant = __webpack_require__(9); const stripBOM = __webpack_require__(160); const noop = () => {}; function copy(src, dest, reporter) { return copyBulk([{ src, dest }], reporter); } function _readFile(loc, encoding) { return new Promise((resolve, reject) => { (_fs || _load_fs()).default.readFile(loc, encoding, function (err, content) { if (err) { reject(err); } else { resolve(content); } }); }); } function readFile(loc) { return _readFile(loc, 'utf8').then(normalizeOS); } function readFileRaw(loc) { return _readFile(loc, 'binary'); } function normalizeOS(body) { return body.replace(/\r\n/g, '\n'); } const cr = '\r'.charCodeAt(0); const lf = '\n'.charCodeAt(0); /***/ }), /* 6 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); class MessageError extends Error { constructor(msg, code) { super(msg); this.code = code; } } exports.MessageError = MessageError; class ProcessSpawnError extends MessageError { constructor(msg, code, process) { super(msg, code); this.process = process; } } exports.ProcessSpawnError = ProcessSpawnError; class SecurityError extends MessageError {} exports.SecurityError = SecurityError; class ProcessTermError extends MessageError {} exports.ProcessTermError = ProcessTermError; class ResponseError extends Error { constructor(msg, responseCode) { super(msg); this.responseCode = responseCode; } } exports.ResponseError = ResponseError; class OneTimePasswordError extends Error {} exports.OneTimePasswordError = OneTimePasswordError; /***/ }), /* 7 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Subscriber; }); /* unused harmony export SafeSubscriber */ /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_tslib__ = __webpack_require__(1); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__util_isFunction__ = __webpack_require__(154); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__Observer__ = __webpack_require__(420); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__Subscription__ = __webpack_require__(25); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__internal_symbol_rxSubscriber__ = __webpack_require__(321); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__config__ = __webpack_require__(186); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__util_hostReportError__ = __webpack_require__(323); /** PURE_IMPORTS_START tslib,_util_isFunction,_Observer,_Subscription,_internal_symbol_rxSubscriber,_config,_util_hostReportError PURE_IMPORTS_END */ var Subscriber = /*@__PURE__*/ (function (_super) { __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](Subscriber, _super); function Subscriber(destinationOrNext, error, complete) { var _this = _super.call(this) || this; _this.syncErrorValue = null; _this.syncErrorThrown = false; _this.syncErrorThrowable = false; _this.isStopped = false; _this._parentSubscription = null; switch (arguments.length) { case 0: _this.destination = __WEBPACK_IMPORTED_MODULE_2__Observer__["a" /* empty */]; break; case 1: if (!destinationOrNext) { _this.destination = __WEBPACK_IMPORTED_MODULE_2__Observer__["a" /* empty */]; break; } if (typeof destinationOrNext === 'object') { if (destinationOrNext instanceof Subscriber) { _this.syncErrorThrowable = destinationOrNext.syncErrorThrowable; _this.destination = destinationOrNext; destinationOrNext.add(_this); } else { _this.syncErrorThrowable = true; _this.destination = new SafeSubscriber(_this, destinationOrNext); } break; } default: _this.syncErrorThrowable = true; _this.destination = new SafeSubscriber(_this, destinationOrNext, error, complete); break; } return _this; } Subscriber.prototype[__WEBPACK_IMPORTED_MODULE_4__internal_symbol_rxSubscriber__["a" /* rxSubscriber */]] = function () { return this; }; Subscriber.create = function (next, error, complete) { var subscriber = new Subscriber(next, error, complete); subscriber.syncErrorThrowable = false; return subscriber; }; Subscriber.prototype.next = function (value) { if (!this.isStopped) { this._next(value); } }; Subscriber.prototype.error = function (err) { if (!this.isStopped) { this.isStopped = true; this._error(err); } }; Subscriber.prototype.complete = function () { if (!this.isStopped) { this.isStopped = true; this._complete(); } }; Subscriber.prototype.unsubscribe = function () { if (this.closed) { return; } this.isStopped = true; _super.prototype.unsubscribe.call(this); }; Subscriber.prototype._next = function (value) { this.destination.next(value); }; Subscriber.prototype._error = function (err) { this.destination.error(err); this.unsubscribe(); }; Subscriber.prototype._complete = function () { this.destination.complete(); this.unsubscribe(); }; Subscriber.prototype._unsubscribeAndRecycle = function () { var _a = this, _parent = _a._parent, _parents = _a._parents; this._parent = null; this._parents = null; this.unsubscribe(); this.closed = false; this.isStopped = false; this._parent = _parent; this._parents = _parents; this._parentSubscription = null; return this; }; return Subscriber; }(__WEBPACK_IMPORTED_MODULE_3__Subscription__["a" /* Subscription */])); var SafeSubscriber = /*@__PURE__*/ (function (_super) { __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](SafeSubscriber, _super); function SafeSubscriber(_parentSubscriber, observerOrNext, error, complete) { var _this = _super.call(this) || this; _this._parentSubscriber = _parentSubscriber; var next; var context = _this; if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__util_isFunction__["a" /* isFunction */])(observerOrNext)) { next = observerOrNext; } else if (observerOrNext) { next = observerOrNext.next; error = observerOrNext.error; complete = observerOrNext.complete; if (observerOrNext !== __WEBPACK_IMPORTED_MODULE_2__Observer__["a" /* empty */]) { context = Object.create(observerOrNext); if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__util_isFunction__["a" /* isFunction */])(context.unsubscribe)) { _this.add(context.unsubscribe.bind(context)); } context.unsubscribe = _this.unsubscribe.bind(_this); } } _this._context = context; _this._next = next; _this._error = error; _this._complete = complete; return _this; } SafeSubscriber.prototype.next = function (value) { if (!this.isStopped && this._next) { var _parentSubscriber = this._parentSubscriber; if (!__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { this.__tryOrUnsub(this._next, value); } else if (this.__tryOrSetError(_parentSubscriber, this._next, value)) { this.unsubscribe(); } } }; SafeSubscriber.prototype.error = function (err) { if (!this.isStopped) { var _parentSubscriber = this._parentSubscriber; var useDeprecatedSynchronousErrorHandling = __WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling; if (this._error) { if (!useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { this.__tryOrUnsub(this._error, err); this.unsubscribe(); } else { this.__tryOrSetError(_parentSubscriber, this._error, err); this.unsubscribe(); } } else if (!_parentSubscriber.syncErrorThrowable) { this.unsubscribe(); if (useDeprecatedSynchronousErrorHandling) { throw err; } __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_6__util_hostReportError__["a" /* hostReportError */])(err); } else { if (useDeprecatedSynchronousErrorHandling) { _parentSubscriber.syncErrorValue = err; _parentSubscriber.syncErrorThrown = true; } else { __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_6__util_hostReportError__["a" /* hostReportError */])(err); } this.unsubscribe(); } } }; SafeSubscriber.prototype.complete = function () { var _this = this; if (!this.isStopped) { var _parentSubscriber = this._parentSubscriber; if (this._complete) { var wrappedComplete = function () { return _this._complete.call(_this._context); }; if (!__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { this.__tryOrUnsub(wrappedComplete); this.unsubscribe(); } else { this.__tryOrSetError(_parentSubscriber, wrappedComplete); this.unsubscribe(); } } else { this.unsubscribe(); } } }; SafeSubscriber.prototype.__tryOrUnsub = function (fn, value) { try { fn.call(this._context, value); } catch (err) { this.unsubscribe(); if (__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { throw err; } else { __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_6__util_hostReportError__["a" /* hostReportError */])(err); } } }; SafeSubscriber.prototype.__tryOrSetError = function (parent, fn, value) { if (!__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { throw new Error('bad call'); } try { fn.call(this._context, value); } catch (err) { if (__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { parent.syncErrorValue = err; parent.syncErrorThrown = true; return true; } else { __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_6__util_hostReportError__["a" /* hostReportError */])(err); return true; } } return false; }; SafeSubscriber.prototype._unsubscribe = function () { var _parentSubscriber = this._parentSubscriber; this._context = null; this._parentSubscriber = null; _parentSubscriber.unsubscribe(); }; return SafeSubscriber; }(Subscriber)); //# sourceMappingURL=Subscriber.js.map /***/ }), /* 8 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getPathKey = getPathKey; const os = __webpack_require__(46); const path = __webpack_require__(0); const userHome = __webpack_require__(67).default; var _require = __webpack_require__(225); const getCacheDir = _require.getCacheDir, getConfigDir = _require.getConfigDir, getDataDir = _require.getDataDir; const isWebpackBundle = __webpack_require__(278); const DEPENDENCY_TYPES = exports.DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'optionalDependencies', 'peerDependencies']; const OWNED_DEPENDENCY_TYPES = exports.OWNED_DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'optionalDependencies']; const RESOLUTIONS = exports.RESOLUTIONS = 'resolutions'; const MANIFEST_FIELDS = exports.MANIFEST_FIELDS = [RESOLUTIONS, ...DEPENDENCY_TYPES]; const SUPPORTED_NODE_VERSIONS = exports.SUPPORTED_NODE_VERSIONS = '^4.8.0 || ^5.7.0 || ^6.2.2 || >=8.0.0'; const YARN_REGISTRY = exports.YARN_REGISTRY = 'https://registry.yarnpkg.com'; const NPM_REGISTRY_RE = exports.NPM_REGISTRY_RE = /https?:\/\/registry\.npmjs\.org/g; const YARN_DOCS = exports.YARN_DOCS = 'https://yarnpkg.com/en/docs/cli/'; const YARN_INSTALLER_SH = exports.YARN_INSTALLER_SH = 'https://yarnpkg.com/install.sh'; const YARN_INSTALLER_MSI = exports.YARN_INSTALLER_MSI = 'https://yarnpkg.com/latest.msi'; const SELF_UPDATE_VERSION_URL = exports.SELF_UPDATE_VERSION_URL = 'https://yarnpkg.com/latest-version'; // cache version, bump whenever we make backwards incompatible changes const CACHE_VERSION = exports.CACHE_VERSION = 6; // lockfile version, bump whenever we make backwards incompatible changes const LOCKFILE_VERSION = exports.LOCKFILE_VERSION = 1; // max amount of network requests to perform concurrently const NETWORK_CONCURRENCY = exports.NETWORK_CONCURRENCY = 8; // HTTP timeout used when downloading packages const NETWORK_TIMEOUT = exports.NETWORK_TIMEOUT = 30 * 1000; // in milliseconds // max amount of child processes to execute concurrently const CHILD_CONCURRENCY = exports.CHILD_CONCURRENCY = 5; const REQUIRED_PACKAGE_KEYS = exports.REQUIRED_PACKAGE_KEYS = ['name', 'version', '_uid']; function getPreferredCacheDirectories() { const preferredCacheDirectories = [getCacheDir()]; if (process.getuid) { // $FlowFixMe: process.getuid exists, dammit preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache-${process.getuid()}`)); } preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache`)); return preferredCacheDirectories; } const PREFERRED_MODULE_CACHE_DIRECTORIES = exports.PREFERRED_MODULE_CACHE_DIRECTORIES = getPreferredCacheDirectories(); const CONFIG_DIRECTORY = exports.CONFIG_DIRECTORY = getConfigDir(); const DATA_DIRECTORY = exports.DATA_DIRECTORY = getDataDir(); const LINK_REGISTRY_DIRECTORY = exports.LINK_REGISTRY_DIRECTORY = path.join(DATA_DIRECTORY, 'link'); const GLOBAL_MODULE_DIRECTORY = exports.GLOBAL_MODULE_DIRECTORY = path.join(DATA_DIRECTORY, 'global'); const NODE_BIN_PATH = exports.NODE_BIN_PATH = process.execPath; const YARN_BIN_PATH = exports.YARN_BIN_PATH = getYarnBinPath(); // Webpack needs to be configured with node.__dirname/__filename = false function getYarnBinPath() { if (isWebpackBundle) { return __filename; } else { return path.join(__dirname, '..', 'bin', 'yarn.js'); } } const NODE_MODULES_FOLDER = exports.NODE_MODULES_FOLDER = 'node_modules'; const NODE_PACKAGE_JSON = exports.NODE_PACKAGE_JSON = 'package.json'; const PNP_FILENAME = exports.PNP_FILENAME = '.pnp.js'; const POSIX_GLOBAL_PREFIX = exports.POSIX_GLOBAL_PREFIX = `${process.env.DESTDIR || ''}/usr/local`; const FALLBACK_GLOBAL_PREFIX = exports.FALLBACK_GLOBAL_PREFIX = path.join(userHome, '.yarn'); const META_FOLDER = exports.META_FOLDER = '.yarn-meta'; const INTEGRITY_FILENAME = exports.INTEGRITY_FILENAME = '.yarn-integrity'; const LOCKFILE_FILENAME = exports.LOCKFILE_FILENAME = 'yarn.lock'; const METADATA_FILENAME = exports.METADATA_FILENAME = '.yarn-metadata.json'; const TARBALL_FILENAME = exports.TARBALL_FILENAME = '.yarn-tarball.tgz'; const CLEAN_FILENAME = exports.CLEAN_FILENAME = '.yarnclean'; const NPM_LOCK_FILENAME = exports.NPM_LOCK_FILENAME = 'package-lock.json'; const NPM_SHRINKWRAP_FILENAME = exports.NPM_SHRINKWRAP_FILENAME = 'npm-shrinkwrap.json'; const DEFAULT_INDENT = exports.DEFAULT_INDENT = ' '; const SINGLE_INSTANCE_PORT = exports.SINGLE_INSTANCE_PORT = 31997; const SINGLE_INSTANCE_FILENAME = exports.SINGLE_INSTANCE_FILENAME = '.yarn-single-instance'; const ENV_PATH_KEY = exports.ENV_PATH_KEY = getPathKey(process.platform, process.env); function getPathKey(platform, env) { let pathKey = 'PATH'; // windows calls its path "Path" usually, but this is not guaranteed. if (platform === 'win32') { pathKey = 'Path'; for (const key in env) { if (key.toLowerCase() === 'path') { pathKey = key; } } } return pathKey; } const VERSION_COLOR_SCHEME = exports.VERSION_COLOR_SCHEME = { major: 'red', premajor: 'red', minor: 'yellow', preminor: 'yellow', patch: 'green', prepatch: 'green', prerelease: 'red', unchanged: 'white', unknown: 'red' }; /***/ }), /* 9 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /** * Use invariant() to assert state which your program assumes to be true. * * Provide sprintf-style format (only %s is supported) and arguments * to provide information about what broke and what you were * expecting. * * The invariant message will be stripped in production, but the invariant * will remain to ensure logic does not differ in production. */ var NODE_ENV = process.env.NODE_ENV; var invariant = function(condition, format, a, b, c, d, e, f) { if (NODE_ENV !== 'production') { if (format === undefined) { throw new Error('invariant requires an error message argument'); } } if (!condition) { var error; if (format === undefined) { error = new Error( 'Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.' ); } else { var args = [a, b, c, d, e, f]; var argIndex = 0; error = new Error( format.replace(/%s/g, function() { return args[argIndex++]; }) ); error.name = 'Invariant Violation'; } error.framesToPop = 1; // we don't care about invariant's own frame throw error; } }; module.exports = invariant; /***/ }), /* 10 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var YAMLException = __webpack_require__(55); var TYPE_CONSTRUCTOR_OPTIONS = [ 'kind', 'resolve', 'construct', 'instanceOf', 'predicate', 'represent', 'defaultStyle', 'styleAliases' ]; var YAML_NODE_KINDS = [ 'scalar', 'sequence', 'mapping' ]; function compileStyleAliases(map) { var result = {}; if (map !== null) { Object.keys(map).forEach(function (style) { map[style].forEach(function (alias) { result[String(alias)] = style; }); }); } return result; } function Type(tag, options) { options = options || {}; Object.keys(options).forEach(function (name) { if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name) === -1) { throw new YAMLException('Unknown option "' + name + '" is met in definition of "' + tag + '" YAML type.'); } }); // TODO: Add tag format check. this.tag = tag; this.kind = options['kind'] || null; this.resolve = options['resolve'] || function () { return true; }; this.construct = options['construct'] || function (data) { return data; }; this.instanceOf = options['instanceOf'] || null; this.predicate = options['predicate'] || null; this.represent = options['represent'] || null; this.defaultStyle = options['defaultStyle'] || null; this.styleAliases = compileStyleAliases(options['styleAliases'] || null); if (YAML_NODE_KINDS.indexOf(this.kind) === -1) { throw new YAMLException('Unknown kind "' + this.kind + '" is specified for "' + tag + '" YAML type.'); } } module.exports = Type; /***/ }), /* 11 */ /***/ (function(module, exports) { module.exports = require("crypto"); /***/ }), /* 12 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Observable; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__util_canReportError__ = __webpack_require__(322); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__util_toSubscriber__ = __webpack_require__(932); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__internal_symbol_observable__ = __webpack_require__(118); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__util_pipe__ = __webpack_require__(324); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__config__ = __webpack_require__(186); /** PURE_IMPORTS_START _util_canReportError,_util_toSubscriber,_internal_symbol_observable,_util_pipe,_config PURE_IMPORTS_END */ var Observable = /*@__PURE__*/ (function () { function Observable(subscribe) { this._isScalar = false; if (subscribe) { this._subscribe = subscribe; } } Observable.prototype.lift = function (operator) { var observable = new Observable(); observable.source = this; observable.operator = operator; return observable; }; Observable.prototype.subscribe = function (observerOrNext, error, complete) { var operator = this.operator; var sink = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__util_toSubscriber__["a" /* toSubscriber */])(observerOrNext, error, complete); if (operator) { operator.call(sink, this.source); } else { sink.add(this.source || (__WEBPACK_IMPORTED_MODULE_4__config__["a" /* config */].useDeprecatedSynchronousErrorHandling && !sink.syncErrorThrowable) ? this._subscribe(sink) : this._trySubscribe(sink)); } if (__WEBPACK_IMPORTED_MODULE_4__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { if (sink.syncErrorThrowable) { sink.syncErrorThrowable = false; if (sink.syncErrorThrown) { throw sink.syncErrorValue; } } } return sink; }; Observable.prototype._trySubscribe = function (sink) { try { return this._subscribe(sink); } catch (err) { if (__WEBPACK_IMPORTED_MODULE_4__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { sink.syncErrorThrown = true; sink.syncErrorValue = err; } if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__util_canReportError__["a" /* canReportError */])(sink)) { sink.error(err); } else { console.warn(err); } } }; Observable.prototype.forEach = function (next, promiseCtor) { var _this = this; promiseCtor = getPromiseCtor(promiseCtor); return new promiseCtor(function (resolve, reject) { var subscription; subscription = _this.subscribe(function (value) { try { next(value); } catch (err) { reject(err); if (subscription) { subscription.unsubscribe(); } } }, reject, resolve); }); }; Observable.prototype._subscribe = function (subscriber) { var source = this.source; return source && source.subscribe(subscriber); }; Observable.prototype[__WEBPACK_IMPORTED_MODULE_2__internal_symbol_observable__["a" /* observable */]] = function () { return this; }; Observable.prototype.pipe = function () { var operations = []; for (var _i = 0; _i < arguments.length; _i++) { operations[_i] = arguments[_i]; } if (operations.length === 0) { return this; } return __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__util_pipe__["b" /* pipeFromArray */])(operations)(this); }; Observable.prototype.toPromise = function (promiseCtor) { var _this = this; promiseCtor = getPromiseCtor(promiseCtor); return new promiseCtor(function (resolve, reject) { var value; _this.subscribe(function (x) { return value = x; }, function (err) { return reject(err); }, function () { return resolve(value); }); }); }; Observable.create = function (subscribe) { return new Observable(subscribe); }; return Observable; }()); function getPromiseCtor(promiseCtor) { if (!promiseCtor) { promiseCtor = __WEBPACK_IMPORTED_MODULE_4__config__["a" /* config */].Promise || Promise; } if (!promiseCtor) { throw new Error('no Promise impl found'); } return promiseCtor; } //# sourceMappingURL=Observable.js.map /***/ }), /* 13 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return OuterSubscriber; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_tslib__ = __webpack_require__(1); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Subscriber__ = __webpack_require__(7); /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ var OuterSubscriber = /*@__PURE__*/ (function (_super) { __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](OuterSubscriber, _super); function OuterSubscriber() { return _super !== null && _super.apply(this, arguments) || this; } OuterSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { this.destination.next(innerValue); }; OuterSubscriber.prototype.notifyError = function (error, innerSub) { this.destination.error(error); }; OuterSubscriber.prototype.notifyComplete = function (innerSub) { this.destination.complete(); }; return OuterSubscriber; }(__WEBPACK_IMPORTED_MODULE_1__Subscriber__["a" /* Subscriber */])); //# sourceMappingURL=OuterSubscriber.js.map /***/ }), /* 14 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (immutable) */ __webpack_exports__["a"] = subscribeToResult; /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__InnerSubscriber__ = __webpack_require__(84); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__subscribeTo__ = __webpack_require__(446); /** PURE_IMPORTS_START _InnerSubscriber,_subscribeTo PURE_IMPORTS_END */ function subscribeToResult(outerSubscriber, result, outerValue, outerIndex, destination) { if (destination === void 0) { destination = new __WEBPACK_IMPORTED_MODULE_0__InnerSubscriber__["a" /* InnerSubscriber */](outerSubscriber, outerValue, outerIndex); } if (destination.closed) { return; } return __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__subscribeTo__["a" /* subscribeTo */])(result)(destination); } //# sourceMappingURL=subscribeToResult.js.map /***/ }), /* 15 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* eslint-disable node/no-deprecated-api */ var buffer = __webpack_require__(64) var Buffer = buffer.Buffer var safer = {} var key for (key in buffer) { if (!buffer.hasOwnProperty(key)) continue if (key === 'SlowBuffer' || key === 'Buffer') continue safer[key] = buffer[key] } var Safer = safer.Buffer = {} for (key in Buffer) { if (!Buffer.hasOwnProperty(key)) continue if (key === 'allocUnsafe' || key === 'allocUnsafeSlow') continue Safer[key] = Buffer[key] } safer.Buffer.prototype = Buffer.prototype if (!Safer.from || Safer.from === Uint8Array.from) { Safer.from = function (value, encodingOrOffset, length) { if (typeof value === 'number') { throw new TypeError('The "value" argument must not be of type number. Received type ' + typeof value) } if (value && typeof value.length === 'undefined') { throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type ' + typeof value) } return Buffer(value, encodingOrOffset, length) } } if (!Safer.alloc) { Safer.alloc = function (size, fill, encoding) { if (typeof size !== 'number') { throw new TypeError('The "size" argument must be of type number. Received type ' + typeof size) } if (size < 0 || size >= 2 * (1 << 30)) { throw new RangeError('The value "' + size + '" is invalid for option "size"') } var buf = Buffer(size) if (!fill || fill.length === 0) { buf.fill(0) } else if (typeof encoding === 'string') { buf.fill(fill, encoding) } else { buf.fill(fill) } return buf } } if (!safer.kStringMaxLength) { try { safer.kStringMaxLength = process.binding('buffer').kStringMaxLength } catch (e) { // we can't determine kStringMaxLength in environments where process.binding // is unsupported, so let's not set it } } if (!safer.constants) { safer.constants = { MAX_LENGTH: safer.kMaxLength } if (safer.kStringMaxLength) { safer.constants.MAX_STRING_LENGTH = safer.kStringMaxLength } } module.exports = safer /***/ }), /* 16 */ /***/ (function(module, exports, __webpack_require__) { // Copyright (c) 2012, Mark Cavage. All rights reserved. // Copyright 2015 Joyent, Inc. var assert = __webpack_require__(28); var Stream = __webpack_require__(23).Stream; var util = __webpack_require__(3); ///--- Globals /* JSSTYLED */ var UUID_REGEXP = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/; ///--- Internal function _capitalize(str) { return (str.charAt(0).toUpperCase() + str.slice(1)); } function _toss(name, expected, oper, arg, actual) { throw new assert.AssertionError({ message: util.format('%s (%s) is required', name, expected), actual: (actual === undefined) ? typeof (arg) : actual(arg), expected: expected, operator: oper || '===', stackStartFunction: _toss.caller }); } function _getClass(arg) { return (Object.prototype.toString.call(arg).slice(8, -1)); } function noop() { // Why even bother with asserts? } ///--- Exports var types = { bool: { check: function (arg) { return typeof (arg) === 'boolean'; } }, func: { check: function (arg) { return typeof (arg) === 'function'; } }, string: { check: function (arg) { return typeof (arg) === 'string'; } }, object: { check: function (arg) { return typeof (arg) === 'object' && arg !== null; } }, number: { check: function (arg) { return typeof (arg) === 'number' && !isNaN(arg); } }, finite: { check: function (arg) { return typeof (arg) === 'number' && !isNaN(arg) && isFinite(arg); } }, buffer: { check: function (arg) { return Buffer.isBuffer(arg); }, operator: 'Buffer.isBuffer' }, array: { check: function (arg) { return Array.isArray(arg); }, operator: 'Array.isArray' }, stream: { check: function (arg) { return arg instanceof Stream; }, operator: 'instanceof', actual: _getClass }, date: { check: function (arg) { return arg instanceof Date; }, operator: 'instanceof', actual: _getClass }, regexp: { check: function (arg) { return arg instanceof RegExp; }, operator: 'instanceof', actual: _getClass }, uuid: { check: function (arg) { return typeof (arg) === 'string' && UUID_REGEXP.test(arg); }, operator: 'isUUID' } }; function _setExports(ndebug) { var keys = Object.keys(types); var out; /* re-export standard assert */ if (process.env.NODE_NDEBUG) { out = noop; } else { out = function (arg, msg) { if (!arg) { _toss(msg, 'true', arg); } }; } /* standard checks */ keys.forEach(function (k) { if (ndebug) { out[k] = noop; return; } var type = types[k]; out[k] = function (arg, msg) { if (!type.check(arg)) { _toss(msg, k, type.operator, arg, type.actual); } }; }); /* optional checks */ keys.forEach(function (k) { var name = 'optional' + _capitalize(k); if (ndebug) { out[name] = noop; return; } var type = types[k]; out[name] = function (arg, msg) { if (arg === undefined || arg === null) { return; } if (!type.check(arg)) { _toss(msg, k, type.operator, arg, type.actual); } }; }); /* arrayOf checks */ keys.forEach(function (k) { var name = 'arrayOf' + _capitalize(k); if (ndebug) { out[name] = noop; return; } var type = types[k]; var expected = '[' + k + ']'; out[name] = function (arg, msg) { if (!Array.isArray(arg)) { _toss(msg, expected, type.operator, arg, type.actual); } var i; for (i = 0; i < arg.length; i++) { if (!type.check(arg[i])) { _toss(msg, expected, type.operator, arg, type.actual); } } }; }); /* optionalArrayOf checks */ keys.forEach(function (k) { var name = 'optionalArrayOf' + _capitalize(k); if (ndebug) { out[name] = noop; return; } var type = types[k]; var expected = '[' + k + ']'; out[name] = function (arg, msg) { if (arg === undefined || arg === null) { return; } if (!Array.isArray(arg)) { _toss(msg, expected, type.operator, arg, type.actual); } var i; for (i = 0; i < arg.length; i++) { if (!type.check(arg[i])) { _toss(msg, expected, type.operator, arg, type.actual); } } }; }); /* re-export built-in assertions */ Object.keys(assert).forEach(function (k) { if (k === 'AssertionError') { out[k] = assert[k]; return; } if (ndebug) { out[k] = noop; return; } out[k] = assert[k]; }); /* export ourselves (for unit tests _only_) */ out._setExports = _setExports; return out; } module.exports = _setExports(process.env.NODE_NDEBUG); /***/ }), /* 17 */ /***/ (function(module, exports) { // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 var global = module.exports = typeof window != 'undefined' && window.Math == Math ? window : typeof self != 'undefined' && self.Math == Math ? self // eslint-disable-next-line no-new-func : Function('return this')(); if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef /***/ }), /* 18 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.sortAlpha = sortAlpha; exports.sortOptionsByFlags = sortOptionsByFlags; exports.entries = entries; exports.removePrefix = removePrefix; exports.removeSuffix = removeSuffix; exports.addSuffix = addSuffix; exports.hyphenate = hyphenate; exports.camelCase = camelCase; exports.compareSortedArrays = compareSortedArrays; exports.sleep = sleep; const _camelCase = __webpack_require__(230); function sortAlpha(a, b) { // sort alphabetically in a deterministic way const shortLen = Math.min(a.length, b.length); for (let i = 0; i < shortLen; i++) { const aChar = a.charCodeAt(i); const bChar = b.charCodeAt(i); if (aChar !== bChar) { return aChar - bChar; } } return a.length - b.length; } function sortOptionsByFlags(a, b) { const aOpt = a.flags.replace(/-/g, ''); const bOpt = b.flags.replace(/-/g, ''); return sortAlpha(aOpt, bOpt); } function entries(obj) { const entries = []; if (obj) { for (const key in obj) { entries.push([key, obj[key]]); } } return entries; } function removePrefix(pattern, prefix) { if (pattern.startsWith(prefix)) { pattern = pattern.slice(prefix.length); } return pattern; } function removeSuffix(pattern, suffix) { if (pattern.endsWith(suffix)) { return pattern.slice(0, -suffix.length); } return pattern; } function addSuffix(pattern, suffix) { if (!pattern.endsWith(suffix)) { return pattern + suffix; } return pattern; } function hyphenate(str) { return str.replace(/[A-Z]/g, match => { return '-' + match.charAt(0).toLowerCase(); }); } function camelCase(str) { if (/[A-Z]/.test(str)) { return null; } else { return _camelCase(str); } } function compareSortedArrays(array1, array2) { if (array1.length !== array2.length) { return false; } for (let i = 0, len = array1.length; i < len; i++) { if (array1[i] !== array2[i]) { return false; } } return true; } function sleep(ms) { return new Promise(resolve => { setTimeout(resolve, ms); }); } /***/ }), /* 19 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.stringify = exports.parse = undefined; var _asyncToGenerator2; function _load_asyncToGenerator() { return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(2)); } var _parse; function _load_parse() { return _parse = __webpack_require__(106); } Object.defineProperty(exports, 'parse', { enumerable: true, get: function get() { return _interopRequireDefault(_parse || _load_parse()).default; } }); var _stringify; function _load_stringify() { return _stringify = __webpack_require__(200); } Object.defineProperty(exports, 'stringify', { enumerable: true, get: function get() { return _interopRequireDefault(_stringify || _load_stringify()).default; } }); exports.implodeEntry = implodeEntry; exports.explodeEntry = explodeEntry; var _misc; function _load_misc() { return _misc = __webpack_require__(18); } var _normalizePattern; function _load_normalizePattern() { return _normalizePattern = __webpack_require__(37); } var _parse2; function _load_parse2() { return _parse2 = _interopRequireDefault(__webpack_require__(106)); } var _constants; function _load_constants() { return _constants = __webpack_require__(8); } var _fs; function _load_fs() { return _fs = _interopRequireWildcard(__webpack_require__(5)); } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const invariant = __webpack_require__(9); const path = __webpack_require__(0); const ssri = __webpack_require__(65); function getName(pattern) { return (0, (_normalizePattern || _load_normalizePattern()).normalizePattern)(pattern).name; } function blankObjectUndefined(obj) { return obj && Object.keys(obj).length ? obj : undefined; } function keyForRemote(remote) { return remote.resolved || (remote.reference && remote.hash ? `${remote.reference}#${remote.hash}` : null); } function serializeIntegrity(integrity) { // We need this because `Integrity.toString()` does not use sorting to ensure a stable string output // See https://git.io/vx2Hy return integrity.toString().split(' ').sort().join(' '); } function implodeEntry(pattern, obj) { const inferredName = getName(pattern); const integrity = obj.integrity ? serializeIntegrity(obj.integrity) : ''; const imploded = { name: inferredName === obj.name ? undefined : obj.name, version: obj.version, uid: obj.uid === obj.version ? undefined : obj.uid, resolved: obj.resolved, registry: obj.registry === 'npm' ? undefined : obj.registry, dependencies: blankObjectUndefined(obj.dependencies), optionalDependencies: blankObjectUndefined(obj.optionalDependencies), permissions: blankObjectUndefined(obj.permissions), prebuiltVariants: blankObjectUndefined(obj.prebuiltVariants) }; if (integrity) { imploded.integrity = integrity; } return imploded; } function explodeEntry(pattern, obj) { obj.optionalDependencies = obj.optionalDependencies || {}; obj.dependencies = obj.dependencies || {}; obj.uid = obj.uid || obj.version; obj.permissions = obj.permissions || {}; obj.registry = obj.registry || 'npm'; obj.name = obj.name || getName(pattern); const integrity = obj.integrity; if (integrity && integrity.isIntegrity) { obj.integrity = ssri.parse(integrity); } return obj; } class Lockfile { constructor({ cache, source, parseResultType } = {}) { this.source = source || ''; this.cache = cache; this.parseResultType = parseResultType; } // source string if the `cache` was parsed // if true, we're parsing an old yarn file and need to update integrity fields hasEntriesExistWithoutIntegrity() { if (!this.cache) { return false; } for (const key in this.cache) { // $FlowFixMe - `this.cache` is clearly defined at this point if (!/^.*@(file:|http)/.test(key) && this.cache[key] && !this.cache[key].integrity) { return true; } } return false; } static fromDirectory(dir, reporter) { return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { // read the manifest in this directory const lockfileLoc = path.join(dir, (_constants || _load_constants()).LOCKFILE_FILENAME); let lockfile; let rawLockfile = ''; let parseResult; if (yield (_fs || _load_fs()).exists(lockfileLoc)) { rawLockfile = yield (_fs || _load_fs()).readFile(lockfileLoc); parseResult = (0, (_parse2 || _load_parse2()).default)(rawLockfile, lockfileLoc); if (reporter) { if (parseResult.type === 'merge') { reporter.info(reporter.lang('lockfileMerged')); } else if (parseResult.type === 'conflict') { reporter.warn(reporter.lang('lockfileConflict')); } } lockfile = parseResult.object; } else if (reporter) { reporter.info(reporter.lang('noLockfileFound')); } if (lockfile && lockfile.__metadata) { const lockfilev2 = lockfile; lockfile = {}; } return new Lockfile({ cache: lockfile, source: rawLockfile, parseResultType: parseResult && parseResult.type }); })(); } getLocked(pattern) { const cache = this.cache; if (!cache) { return undefined; } const shrunk = pattern in cache && cache[pattern]; if (typeof shrunk === 'string') { return this.getLocked(shrunk); } else if (shrunk) { explodeEntry(pattern, shrunk); return shrunk; } return undefined; } removePattern(pattern) { const cache = this.cache; if (!cache) { return; } delete cache[pattern]; } getLockfile(patterns) { const lockfile = {}; const seen = new Map(); // order by name so that lockfile manifest is assigned to the first dependency with this manifest // the others that have the same remoteKey will just refer to the first // ordering allows for consistency in lockfile when it is serialized const sortedPatternsKeys = Object.keys(patterns).sort((_misc || _load_misc()).sortAlpha); for (var _iterator = sortedPatternsKeys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var _ref; if (_isArray) { if (_i >= _iterator.length) break; _ref = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) break; _ref = _i.value; } const pattern = _ref; const pkg = patterns[pattern]; const remote = pkg._remote, ref = pkg._reference; invariant(ref, 'Package is missing a reference'); invariant(remote, 'Package is missing a remote'); const remoteKey = keyForRemote(remote); const seenPattern = remoteKey && seen.get(remoteKey); if (seenPattern) { // no point in duplicating it lockfile[pattern] = seenPattern; // if we're relying on our name being inferred and two of the patterns have // different inferred names then we need to set it if (!seenPattern.name && getName(pattern) !== pkg.name) { seenPattern.name = pkg.name; } continue; } const obj = implodeEntry(pattern, { name: pkg.name, version: pkg.version, uid: pkg._uid, resolved: remote.resolved, integrity: remote.integrity, registry: remote.registry, dependencies: pkg.dependencies, peerDependencies: pkg.peerDependencies, optionalDependencies: pkg.optionalDependencies, permissions: ref.permissions, prebuiltVariants: pkg.prebuiltVariants }); lockfile[pattern] = obj; if (remoteKey) { seen.set(remoteKey, obj); } } return lockfile; } } exports.default = Lockfile; /***/ }), /* 20 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; var _assign = __webpack_require__(591); var _assign2 = _interopRequireDefault(_assign); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } exports.default = _assign2.default || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /***/ }), /* 21 */ /***/ (function(module, exports, __webpack_require__) { var store = __webpack_require__(133)('wks'); var uid = __webpack_require__(137); var Symbol = __webpack_require__(17).Symbol; var USE_SYMBOL = typeof Symbol == 'function'; var $exports = module.exports = function (name) { return store[name] || (store[name] = USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name)); }; $exports.store = store; /***/ }), /* 22 */ /***/ (function(module, exports) { exports = module.exports = SemVer; // The debug function is excluded entirely from the minified version. /* nomin */ var debug; /* nomin */ if (typeof process === 'object' && /* nomin */ process.env && /* nomin */ process.env.NODE_DEBUG && /* nomin */ /\bsemver\b/i.test(process.env.NODE_DEBUG)) /* nomin */ debug = function() { /* nomin */ var args = Array.prototype.slice.call(arguments, 0); /* nomin */ args.unshift('SEMVER'); /* nomin */ console.log.apply(console, args); /* nomin */ }; /* nomin */ else /* nomin */ debug = function() {}; // Note: this is the semver.org version of the spec that it implements // Not necessarily the package version of this code. exports.SEMVER_SPEC_VERSION = '2.0.0'; var MAX_LENGTH = 256; var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; // Max safe segment length for coercion. var MAX_SAFE_COMPONENT_LENGTH = 16; // The actual regexps go on exports.re var re = exports.re = []; var src = exports.src = []; var R = 0; // The following Regular Expressions can be used for tokenizing, // validating, and parsing SemVer version strings. // ## Numeric Identifier // A single `0`, or a non-zero digit followed by zero or more digits. var NUMERICIDENTIFIER = R++; src[NUMERICIDENTIFIER] = '0|[1-9]\\d*'; var NUMERICIDENTIFIERLOOSE = R++; src[NUMERICIDENTIFIERLOOSE] = '[0-9]+'; // ## Non-numeric Identifier // Zero or more digits, followed by a letter or hyphen, and then zero or // more letters, digits, or hyphens. var NONNUMERICIDENTIFIER = R++; src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*'; // ## Main Version // Three dot-separated numeric identifiers. var MAINVERSION = R++; src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + '(' + src[NUMERICIDENTIFIER] + ')\\.' + '(' + src[NUMERICIDENTIFIER] + ')'; var MAINVERSIONLOOSE = R++; src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + '(' + src[NUMERICIDENTIFIERLOOSE] + ')'; // ## Pre-release Version Identifier // A numeric identifier, or a non-numeric identifier. var PRERELEASEIDENTIFIER = R++; src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + '|' + src[NONNUMERICIDENTIFIER] + ')'; var PRERELEASEIDENTIFIERLOOSE = R++; src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + '|' + src[NONNUMERICIDENTIFIER] + ')'; // ## Pre-release Version // Hyphen, followed by one or more dot-separated pre-release version // identifiers. var PRERELEASE = R++; src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))'; var PRERELEASELOOSE = R++; src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))'; // ## Build Metadata Identifier // Any combination of digits, letters, or hyphens. var BUILDIDENTIFIER = R++; src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+'; // ## Build Metadata // Plus sign, followed by one or more period-separated build metadata // identifiers. var BUILD = R++; src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))'; // ## Full Version String // A main version, followed optionally by a pre-release version and // build metadata. // Note that the only major, minor, patch, and pre-release sections of // the version string are capturing groups. The build metadata is not a // capturing group, because it should not ever be used in version // comparison. var FULL = R++; var FULLPLAIN = 'v?' + src[MAINVERSION] + src[PRERELEASE] + '?' + src[BUILD] + '?'; src[FULL] = '^' + FULLPLAIN + '$'; // like full, but allows v1.2.3 and =1.2.3, which people do sometimes. // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty // common in the npm registry. var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + src[PRERELEASELOOSE] + '?' + src[BUILD] + '?'; var LOOSE = R++; src[LOOSE] = '^' + LOOSEPLAIN + '$'; var GTLT = R++; src[GTLT] = '((?:<|>)?=?)'; // Something like "2.*" or "1.2.x". // Note that "x.x" is a valid xRange identifer, meaning "any version" // Only the first item is strictly required. var XRANGEIDENTIFIERLOOSE = R++; src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*'; var XRANGEIDENTIFIER = R++; src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*'; var XRANGEPLAIN = R++; src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:' + src[PRERELEASE] + ')?' + src[BUILD] + '?' + ')?)?'; var XRANGEPLAINLOOSE = R++; src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:' + src[PRERELEASELOOSE] + ')?' + src[BUILD] + '?' + ')?)?'; var XRANGE = R++; src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$'; var XRANGELOOSE = R++; src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$'; // Coercion. // Extract anything that could conceivably be a part of a valid semver var COERCE = R++; src[COERCE] = '(?:^|[^\\d])' + '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + '(?:$|[^\\d])'; // Tilde ranges. // Meaning is "reasonably at or greater than" var LONETILDE = R++; src[LONETILDE] = '(?:~>?)'; var TILDETRIM = R++; src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+'; re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g'); var tildeTrimReplace = '$1~'; var TILDE = R++; src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$'; var TILDELOOSE = R++; src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$'; // Caret ranges. // Meaning is "at least and backwards compatible with" var LONECARET = R++; src[LONECARET] = '(?:\\^)'; var CARETTRIM = R++; src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+'; re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g'); var caretTrimReplace = '$1^'; var CARET = R++; src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$'; var CARETLOOSE = R++; src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$'; // A simple gt/lt/eq thing, or just "" to indicate "any version" var COMPARATORLOOSE = R++; src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$'; var COMPARATOR = R++; src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$'; // An expression to strip any whitespace between the gtlt and the thing // it modifies, so that `> 1.2.3` ==> `>1.2.3` var COMPARATORTRIM = R++; src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')'; // this one has to use the /g flag re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g'); var comparatorTrimReplace = '$1$2$3'; // Something like `1.2.3 - 1.2.4` // Note that these all use the loose form, because they'll be // checked against either the strict or loose comparator form // later. var HYPHENRANGE = R++; src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + '\\s+-\\s+' + '(' + src[XRANGEPLAIN] + ')' + '\\s*$'; var HYPHENRANGELOOSE = R++; src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + '\\s+-\\s+' + '(' + src[XRANGEPLAINLOOSE] + ')' + '\\s*$'; // Star ranges basically just allow anything at all. var STAR = R++; src[STAR] = '(<|>)?=?\\s*\\*'; // Compile to actual regexp objects. // All are flag-free, unless they were created above with a flag. for (var i = 0; i < R; i++) { debug(i, src[i]); if (!re[i]) re[i] = new RegExp(src[i]); } exports.parse = parse; function parse(version, loose) { if (version instanceof SemVer) return version; if (typeof version !== 'string') return null; if (version.length > MAX_LENGTH) return null; var r = loose ? re[LOOSE] : re[FULL]; if (!r.test(version)) return null; try { return new SemVer(version, loose); } catch (er) { return null; } } exports.valid = valid; function valid(version, loose) { var v = parse(version, loose); return v ? v.version : null; } exports.clean = clean; function clean(version, loose) { var s = parse(version.trim().replace(/^[=v]+/, ''), loose); return s ? s.version : null; } exports.SemVer = SemVer; function SemVer(version, loose) { if (version instanceof SemVer) { if (version.loose === loose) return version; else version = version.version; } else if (typeof version !== 'string') { throw new TypeError('Invalid Version: ' + version); } if (version.length > MAX_LENGTH) throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') if (!(this instanceof SemVer)) return new SemVer(version, loose); debug('SemVer', version, loose); this.loose = loose; var m = version.trim().match(loose ? re[LOOSE] : re[FULL]); if (!m) throw new TypeError('Invalid Version: ' + version); this.raw = version; // these are actually numbers this.major = +m[1]; this.minor = +m[2]; this.patch = +m[3]; if (this.major > MAX_SAFE_INTEGER || this.major < 0) throw new TypeError('Invalid major version') if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) throw new TypeError('Invalid minor version') if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) throw new TypeError('Invalid patch version') // numberify any prerelease numeric ids if (!m[4]) this.prerelease = []; else this.prerelease = m[4].split('.').map(function(id) { if (/^[0-9]+$/.test(id)) { var num = +id; if (num >= 0 && num < MAX_SAFE_INTEGER) return num; } return id; }); this.build = m[5] ? m[5].split('.') : []; this.format(); } SemVer.prototype.format = function() { this.version = this.major + '.' + this.minor + '.' + this.patch; if (this.prerelease.length) this.version += '-' + this.prerelease.join('.'); return this.version; }; SemVer.prototype.toString = function() { return this.version; }; SemVer.prototype.compare = function(other) { debug('SemVer.compare', this.version, this.loose, other); if (!(other instanceof SemVer)) other = new SemVer(other, this.loose); return this.compareMain(other) || this.comparePre(other); }; SemVer.prototype.compareMain = function(other) { if (!(other instanceof SemVer)) other = new SemVer(other, this.loose); return compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || compareIdentifiers(this.patch, other.patch); }; SemVer.prototype.comparePre = function(other) { if (!(other instanceof SemVer)) other = new SemVer(other, this.loose); // NOT having a prerelease is > having one if (this.prerelease.length && !other.prerelease.length) return -1; else if (!this.prerelease.length && other.prerelease.length) return 1; else if (!this.prerelease.length && !other.prerelease.length) return 0; var i = 0; do { var a = this.prerelease[i]; var b = other.prerelease[i]; debug('prerelease compare', i, a, b); if (a === undefined && b === undefined) return 0; else if (b === undefined) return 1; else if (a === undefined) return -1; else if (a === b) continue; else return compareIdentifiers(a, b); } while (++i); }; // preminor will bump the version up to the next minor release, and immediately // down to pre-release. premajor and prepatch work the same way. SemVer.prototype.inc = function(release, identifier) { switch (release) { case 'premajor': this.prerelease.length = 0; this.patch = 0; this.minor = 0; this.major++; this.inc('pre', identifier); break; case 'preminor': this.prerelease.length = 0; this.patch = 0; this.minor++; this.inc('pre', identifier); break; case 'prepatch': // If this is already a prerelease, it will bump to the next version // drop any prereleases that might already exist, since they are not // relevant at this point. this.prerelease.length = 0; this.inc('patch', identifier); this.inc('pre', identifier); break; // If the input is a non-prerelease version, this acts the same as // prepatch. case 'prerelease': if (this.prerelease.length === 0) this.inc('patch', identifier); this.inc('pre', identifier); break; case 'major': // If this is a pre-major version, bump up to the same major version. // Otherwise increment major. // 1.0.0-5 bumps to 1.0.0 // 1.1.0 bumps to 2.0.0 if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0) this.major++; this.minor = 0; this.patch = 0; this.prerelease = []; break; case 'minor': // If this is a pre-minor version, bump up to the same minor version. // Otherwise increment minor. // 1.2.0-5 bumps to 1.2.0 // 1.2.1 bumps to 1.3.0 if (this.patch !== 0 || this.prerelease.length === 0) this.minor++; this.patch = 0; this.prerelease = []; break; case 'patch': // If this is not a pre-release version, it will increment the patch. // If it is a pre-release it will bump up to the same patch version. // 1.2.0-5 patches to 1.2.0 // 1.2.0 patches to 1.2.1 if (this.prerelease.length === 0) this.patch++; this.prerelease = []; break; // This probably shouldn't be used publicly. // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. case 'pre': if (this.prerelease.length === 0) this.prerelease = [0]; else { var i = this.prerelease.length; while (--i >= 0) { if (typeof this.prerelease[i] === 'number') { this.prerelease[i]++; i = -2; } } if (i === -1) // didn't increment anything this.prerelease.push(0); } if (identifier) { // 1.2.0-beta.1 bumps to 1.2.0-beta.2, // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 if (this.prerelease[0] === identifier) { if (isNaN(this.prerelease[1])) this.prerelease = [identifier, 0]; } else this.prerelease = [identifier, 0]; } break; default: throw new Error('invalid increment argument: ' + release); } this.format(); this.raw = this.version; return this; }; exports.inc = inc; function inc(version, release, loose, identifier) { if (typeof(loose) === 'string') { identifier = loose; loose = undefined; } try { return new SemVer(version, loose).inc(release, identifier).version; } catch (er) { return null; } } exports.diff = diff; function diff(version1, version2) { if (eq(version1, version2)) { return null; } else { var v1 = parse(version1); var v2 = parse(version2); if (v1.prerelease.length || v2.prerelease.length) { for (var key in v1) { if (key === 'major' || key === 'minor' || key === 'patch') { if (v1[key] !== v2[key]) { return 'pre'+key; } } } return 'prerelease'; } for (var key in v1) { if (key === 'major' || key === 'minor' || key === 'patch') { if (v1[key] !== v2[key]) { return key; } } } } } exports.compareIdentifiers = compareIdentifiers; var numeric = /^[0-9]+$/; function compareIdentifiers(a, b) { var anum = numeric.test(a); var bnum = numeric.test(b); if (anum && bnum) { a = +a; b = +b; } return (anum && !bnum) ? -1 : (bnum && !anum) ? 1 : a < b ? -1 : a > b ? 1 : 0; } exports.rcompareIdentifiers = rcompareIdentifiers; function rcompareIdentifiers(a, b) { return compareIdentifiers(b, a); } exports.major = major; function major(a, loose) { return new SemVer(a, loose).major; } exports.minor = minor; function minor(a, loose) { return new SemVer(a, loose).minor; } exports.patch = patch; function patch(a, loose) { return new SemVer(a, loose).patch; } exports.compare = compare; function compare(a, b, loose) { return new SemVer(a, loose).compare(new SemVer(b, loose)); } exports.compareLoose = compareLoose; function compareLoose(a, b) { return compare(a, b, true); } exports.rcompare = rcompare; function rcompare(a, b, loose) { return compare(b, a, loose); } exports.sort = sort; function sort(list, loose) { return list.sort(function(a, b) { return exports.compare(a, b, loose); }); } exports.rsort = rsort; function rsort(list, loose) { return list.sort(function(a, b) { return exports.rcompare(a, b, loose); }); } exports.gt = gt; function gt(a, b, loose) { return compare(a, b, loose) > 0; } exports.lt = lt; function lt(a, b, loose) { return compare(a, b, loose) < 0; } exports.eq = eq; function eq(a, b, loose) { return compare(a, b, loose) === 0; } exports.neq = neq; function neq(a, b, loose) { return compare(a, b, loose) !== 0; } exports.gte = gte; function gte(a, b, loose) { return compare(a, b, loose) >= 0; } exports.lte = lte; function lte(a, b, loose) { return compare(a, b, loose) <= 0; } exports.cmp = cmp; function cmp(a, op, b, loose) { var ret; switch (op) { case '===': if (typeof a === 'object') a = a.version; if (typeof b === 'object') b = b.version; ret = a === b; break; case '!==': if (typeof a === 'object') a = a.version; if (typeof b === 'object') b = b.version; ret = a !== b; break; case '': case '=': case '==': ret = eq(a, b, loose); break; case '!=': ret = neq(a, b, loose); break; case '>': ret = gt(a, b, loose); break; case '>=': ret = gte(a, b, loose); break; case '<': ret = lt(a, b, loose); break; case '<=': ret = lte(a, b, loose); break; default: throw new TypeError('Invalid operator: ' + op); } return ret; } exports.Comparator = Comparator; function Comparator(comp, loose) { if (comp instanceof Comparator) { if (comp.loose === loose) return comp; else comp = comp.value; } if (!(this instanceof Comparator)) return new Comparator(comp, loose); debug('comparator', comp, loose); this.loose = loose; this.parse(comp); if (this.semver === ANY) this.value = ''; else this.value = this.operator + this.semver.version; debug('comp', this); } var ANY = {}; Comparator.prototype.parse = function(comp) { var r = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; var m = comp.match(r); if (!m) throw new TypeError('Invalid comparator: ' + comp); this.operator = m[1]; if (this.operator === '=') this.operator = ''; // if it literally is just '>' or '' then allow anything. if (!m[2]) this.semver = ANY; else this.semver = new SemVer(m[2], this.loose); }; Comparator.prototype.toString = function() { return this.value; }; Comparator.prototype.test = function(version) { debug('Comparator.test', version, this.loose); if (this.semver === ANY) return true; if (typeof version === 'string') version = new SemVer(version, this.loose); return cmp(version, this.operator, this.semver, this.loose); }; Comparator.prototype.intersects = function(comp, loose) { if (!(comp instanceof Comparator)) { throw new TypeError('a Comparator is required'); } var rangeTmp; if (this.operator === '') { rangeTmp = new Range(comp.value, loose); return satisfies(this.value, rangeTmp, loose); } else if (comp.operator === '') { rangeTmp = new Range(this.value, loose); return satisfies(comp.semver, rangeTmp, loose); } var sameDirectionIncreasing = (this.operator === '>=' || this.operator === '>') && (comp.operator === '>=' || comp.operator === '>'); var sameDirectionDecreasing = (this.operator === '<=' || this.operator === '<') && (comp.operator === '<=' || comp.operator === '<'); var sameSemVer = this.semver.version === comp.semver.version; var differentDirectionsInclusive = (this.operator === '>=' || this.operator === '<=') && (comp.operator === '>=' || comp.operator === '<='); var oppositeDirectionsLessThan = cmp(this.semver, '<', comp.semver, loose) && ((this.operator === '>=' || this.operator === '>') && (comp.operator === '<=' || comp.operator === '<')); var oppositeDirectionsGreaterThan = cmp(this.semver, '>', comp.semver, loose) && ((this.operator === '<=' || this.operator === '<') && (comp.operator === '>=' || comp.operator === '>')); return sameDirectionIncreasing || sameDirectionDecreasing || (sameSemVer && differentDirectionsInclusive) || oppositeDirectionsLessThan || oppositeDirectionsGreaterThan; }; exports.Range = Range; function Range(range, loose) { if (range instanceof Range) { if (range.loose === loose) { return range; } else { return new Range(range.raw, loose); } } if (range instanceof Comparator) { return new Range(range.value, loose); } if (!(this instanceof Range)) return new Range(range, loose); this.loose = loose; // First, split based on boolean or || this.raw = range; this.set = range.split(/\s*\|\|\s*/).map(function(range) { return this.parseRange(range.trim()); }, this).filter(function(c) { // throw out any that are not relevant for whatever reason return c.length; }); if (!this.set.length) { throw new TypeError('Invalid SemVer Range: ' + range); } this.format(); } Range.prototype.format = function() { this.range = this.set.map(function(comps) { return comps.join(' ').trim(); }).join('||').trim(); return this.range; }; Range.prototype.toString = function() { return this.range; }; Range.prototype.parseRange = function(range) { var loose = this.loose; range = range.trim(); debug('range', range, loose); // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE]; range = range.replace(hr, hyphenReplace); debug('hyphen replace', range); // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace); debug('comparator trim', range, re[COMPARATORTRIM]); // `~ 1.2.3` => `~1.2.3` range = range.replace(re[TILDETRIM], tildeTrimReplace); // `^ 1.2.3` => `^1.2.3` range = range.replace(re[CARETTRIM], caretTrimReplace); // normalize spaces range = range.split(/\s+/).join(' '); // At this point, the range is completely trimmed and // ready to be split into comparators. var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; var set = range.split(' ').map(function(comp) { return parseComparator(comp, loose); }).join(' ').split(/\s+/); if (this.loose) { // in loose mode, throw out any that are not valid comparators set = set.filter(function(comp) { return !!comp.match(compRe); }); } set = set.map(function(comp) { return new Comparator(comp, loose); }); return set; }; Range.prototype.intersects = function(range, loose) { if (!(range instanceof Range)) { throw new TypeError('a Range is required'); } return this.set.some(function(thisComparators) { return thisComparators.every(function(thisComparator) { return range.set.some(function(rangeComparators) { return rangeComparators.every(function(rangeComparator) { return thisComparator.intersects(rangeComparator, loose); }); }); }); }); }; // Mostly just for testing and legacy API reasons exports.toComparators = toComparators; function toComparators(range, loose) { return new Range(range, loose).set.map(function(comp) { return comp.map(function(c) { return c.value; }).join(' ').trim().split(' '); }); } // comprised of xranges, tildes, stars, and gtlt's at this point. // already replaced the hyphen ranges // turn into a set of JUST comparators. function parseComparator(comp, loose) { debug('comp', comp); comp = replaceCarets(comp, loose); debug('caret', comp); comp = replaceTildes(comp, loose); debug('tildes', comp); comp = replaceXRanges(comp, loose); debug('xrange', comp); comp = replaceStars(comp, loose); debug('stars', comp); return comp; } function isX(id) { return !id || id.toLowerCase() === 'x' || id === '*'; } // ~, ~> --> * (any, kinda silly) // ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 // ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 function replaceTildes(comp, loose) { return comp.trim().split(/\s+/).map(function(comp) { return replaceTilde(comp, loose); }).join(' '); } function replaceTilde(comp, loose) { var r = loose ? re[TILDELOOSE] : re[TILDE]; return comp.replace(r, function(_, M, m, p, pr) { debug('tilde', comp, _, M, m, p, pr); var ret; if (isX(M)) ret = ''; else if (isX(m)) ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; else if (isX(p)) // ~1.2 == >=1.2.0 <1.3.0 ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; else if (pr) { debug('replaceTilde pr', pr); if (pr.charAt(0) !== '-') pr = '-' + pr; ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + (+m + 1) + '.0'; } else // ~1.2.3 == >=1.2.3 <1.3.0 ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + (+m + 1) + '.0'; debug('tilde return', ret); return ret; }); } // ^ --> * (any, kinda silly) // ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 // ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 // ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 // ^1.2.3 --> >=1.2.3 <2.0.0 // ^1.2.0 --> >=1.2.0 <2.0.0 function replaceCarets(comp, loose) { return comp.trim().split(/\s+/).map(function(comp) { return replaceCaret(comp, loose); }).join(' '); } function replaceCaret(comp, loose) { debug('caret', comp, loose); var r = loose ? re[CARETLOOSE] : re[CARET]; return comp.replace(r, function(_, M, m, p, pr) { debug('caret', comp, _, M, m, p, pr); var ret; if (isX(M)) ret = ''; else if (isX(m)) ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; else if (isX(p)) { if (M === '0') ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; else ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0'; } else if (pr) { debug('replaceCaret pr', pr); if (pr.charAt(0) !== '-') pr = '-' + pr; if (M === '0') { if (m === '0') ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + m + '.' + (+p + 1); else ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + (+m + 1) + '.0'; } else ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + (+M + 1) + '.0.0'; } else { debug('no pr'); if (M === '0') { if (m === '0') ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + m + '.' + (+p + 1); else ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + (+m + 1) + '.0'; } else ret = '>=' + M + '.' + m + '.' + p + ' <' + (+M + 1) + '.0.0'; } debug('caret return', ret); return ret; }); } function replaceXRanges(comp, loose) { debug('replaceXRanges', comp, loose); return comp.split(/\s+/).map(function(comp) { return replaceXRange(comp, loose); }).join(' '); } function replaceXRange(comp, loose) { comp = comp.trim(); var r = loose ? re[XRANGELOOSE] : re[XRANGE]; return comp.replace(r, function(ret, gtlt, M, m, p, pr) { debug('xRange', comp, ret, gtlt, M, m, p, pr); var xM = isX(M); var xm = xM || isX(m); var xp = xm || isX(p); var anyX = xp; if (gtlt === '=' && anyX) gtlt = ''; if (xM) { if (gtlt === '>' || gtlt === '<') { // nothing is allowed ret = '<0.0.0'; } else { // nothing is forbidden ret = '*'; } } else if (gtlt && anyX) { // replace X with 0 if (xm) m = 0; if (xp) p = 0; if (gtlt === '>') { // >1 => >=2.0.0 // >1.2 => >=1.3.0 // >1.2.3 => >= 1.2.4 gtlt = '>='; if (xm) { M = +M + 1; m = 0; p = 0; } else if (xp) { m = +m + 1; p = 0; } } else if (gtlt === '<=') { // <=0.7.x is actually <0.8.0, since any 0.7.x should // pass. Similarly, <=7.x is actually <8.0.0, etc. gtlt = '<'; if (xm) M = +M + 1; else m = +m + 1; } ret = gtlt + M + '.' + m + '.' + p; } else if (xm) { ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; } else if (xp) { ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; } debug('xRange return', ret); return ret; }); } // Because * is AND-ed with everything else in the comparator, // and '' means "any version", just remove the *s entirely. function replaceStars(comp, loose) { debug('replaceStars', comp, loose); // Looseness is ignored here. star is always as loose as it gets! return comp.trim().replace(re[STAR], ''); } // This function is passed to string.replace(re[HYPHENRANGE]) // M, m, patch, prerelease, build // 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 // 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do // 1.2 - 3.4 => >=1.2.0 <3.5.0 function hyphenReplace($0, from, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr, tb) { if (isX(fM)) from = ''; else if (isX(fm)) from = '>=' + fM + '.0.0'; else if (isX(fp)) from = '>=' + fM + '.' + fm + '.0'; else from = '>=' + from; if (isX(tM)) to = ''; else if (isX(tm)) to = '<' + (+tM + 1) + '.0.0'; else if (isX(tp)) to = '<' + tM + '.' + (+tm + 1) + '.0'; else if (tpr) to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr; else to = '<=' + to; return (from + ' ' + to).trim(); } // if ANY of the sets match ALL of its comparators, then pass Range.prototype.test = function(version) { if (!version) return false; if (typeof version === 'string') version = new SemVer(version, this.loose); for (var i = 0; i < this.set.length; i++) { if (testSet(this.set[i], version)) return true; } return false; }; function testSet(set, version) { for (var i = 0; i < set.length; i++) { if (!set[i].test(version)) return false; } if (version.prerelease.length) { // Find the set of versions that are allowed to have prereleases // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 // That should allow `1.2.3-pr.2` to pass. // However, `1.2.4-alpha.notready` should NOT be allowed, // even though it's within the range set by the comparators. for (var i = 0; i < set.length; i++) { debug(set[i].semver); if (set[i].semver === ANY) continue; if (set[i].semver.prerelease.length > 0) { var allowed = set[i].semver; if (allowed.major === version.major && allowed.minor === version.minor && allowed.patch === version.patch) return true; } } // Version has a -pre, but it's not one of the ones we like. return false; } return true; } exports.satisfies = satisfies; function satisfies(version, range, loose) { try { range = new Range(range, loose); } catch (er) { return false; } return range.test(version); } exports.maxSatisfying = maxSatisfying; function maxSatisfying(versions, range, loose) { var max = null; var maxSV = null; try { var rangeObj = new Range(range, loose); } catch (er) { return null; } versions.forEach(function (v) { if (rangeObj.test(v)) { // satisfies(v, range, loose) if (!max || maxSV.compare(v) === -1) { // compare(max, v, true) max = v; maxSV = new SemVer(max, loose); } } }) return max; } exports.minSatisfying = minSatisfying; function minSatisfying(versions, range, loose) { var min = null; var minSV = null; try { var rangeObj = new Range(range, loose); } catch (er) { return null; } versions.forEach(function (v) { if (rangeObj.test(v)) { // satisfies(v, range, loose) if (!min || minSV.compare(v) === 1) { // compare(min, v, true) min = v; minSV = new SemVer(min, loose); } } }) return min; } exports.validRange = validRange; function validRange(range, loose) { try { // Return '*' instead of '' so that truthiness works. // This will throw if it's invalid anyway return new Range(range, loose).range || '*'; } catch (er) { return null; } } // Determine if version is less than all the versions possible in the range exports.ltr = ltr; function ltr(version, range, loose) { return outside(version, range, '<', loose); } // Determine if version is greater than all the versions possible in the range. exports.gtr = gtr; function gtr(version, range, loose) { return outside(version, range, '>', loose); } exports.outside = outside; function outside(version, range, hilo, loose) { version = new SemVer(version, loose); range = new Range(range, loose); var gtfn, ltefn, ltfn, comp, ecomp; switch (hilo) { case '>': gtfn = gt; ltefn = lte; ltfn = lt; comp = '>'; ecomp = '>='; break; case '<': gtfn = lt; ltefn = gte; ltfn = gt; comp = '<'; ecomp = '<='; break; default: throw new TypeError('Must provide a hilo val of "<" or ">"'); } // If it satisifes the range it is not outside if (satisfies(version, range, loose)) { return false; } // From now on, variable terms are as if we're in "gtr" mode. // but note that everything is flipped for the "ltr" function. for (var i = 0; i < range.set.length; ++i) { var comparators = range.set[i]; var high = null; var low = null; comparators.forEach(function(comparator) { if (comparator.semver === ANY) { comparator = new Comparator('>=0.0.0') } high = high || comparator; low = low || comparator; if (gtfn(comparator.semver, high.semver, loose)) { high = comparator; } else if (ltfn(comparator.semver, low.semver, loose)) { low = comparator; } }); // If the edge version comparator has a operator then our version // isn't outside it if (high.operator === comp || high.operator === ecomp) { return false; } // If the lowest version comparator has an operator and our version // is less than it then it isn't higher than the range if ((!low.operator || low.operator === comp) && ltefn(version, low.semver)) { return false; } else if (low.operator === ecomp && ltfn(version, low.semver)) { return false; } } return true; } exports.prerelease = prerelease; function prerelease(version, loose) { var parsed = parse(version, loose); return (parsed && parsed.prerelease.length) ? parsed.prerelease : null; } exports.intersects = intersects; function intersects(r1, r2, loose) { r1 = new Range(r1, loose) r2 = new Range(r2, loose) return r1.intersects(r2) } exports.coerce = coerce; function coerce(version) { if (version instanceof SemVer) return version; if (typeof version !== 'string') return null; var match = version.match(re[COERCE]); if (match == null) return null; return parse((match[1] || '0') + '.' + (match[2] || '0') + '.' + (match[3] || '0')); } /***/ }), /* 23 */ /***/ (function(module, exports) { module.exports = require("stream"); /***/ }), /* 24 */ /***/ (function(module, exports) { module.exports = require("url"); /***/ }), /* 25 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Subscription; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__util_isArray__ = __webpack_require__(41); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__util_isObject__ = __webpack_require__(444); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__util_isFunction__ = __webpack_require__(154); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__util_tryCatch__ = __webpack_require__(57); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__util_errorObject__ = __webpack_require__(48); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__ = __webpack_require__(441); /** PURE_IMPORTS_START _util_isArray,_util_isObject,_util_isFunction,_util_tryCatch,_util_errorObject,_util_UnsubscriptionError PURE_IMPORTS_END */ var Subscription = /*@__PURE__*/ (function () { function Subscription(unsubscribe) { this.closed = false; this._parent = null; this._parents = null; this._subscriptions = null; if (unsubscribe) { this._unsubscribe = unsubscribe; } } Subscription.prototype.unsubscribe = function () { var hasErrors = false; var errors; if (this.closed) { return; } var _a = this, _parent = _a._parent, _parents = _a._parents, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions; this.closed = true; this._parent = null; this._parents = null; this._subscriptions = null; var index = -1; var len = _parents ? _parents.length : 0; while (_parent) { _parent.remove(this); _parent = ++index < len && _parents[index] || null; } if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__util_isFunction__["a" /* isFunction */])(_unsubscribe)) { var trial = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__util_tryCatch__["a" /* tryCatch */])(_unsubscribe).call(this); if (trial === __WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */]) { hasErrors = true; errors = errors || (__WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */].e instanceof __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__["a" /* UnsubscriptionError */] ? flattenUnsubscriptionErrors(__WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */].e.errors) : [__WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */].e]); } } if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__util_isArray__["a" /* isArray */])(_subscriptions)) { index = -1; len = _subscriptions.length; while (++index < len) { var sub = _subscriptions[index]; if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__util_isObject__["a" /* isObject */])(sub)) { var trial = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__util_tryCatch__["a" /* tryCatch */])(sub.unsubscribe).call(sub); if (trial === __WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */]) { hasErrors = true; errors = errors || []; var err = __WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */].e; if (err instanceof __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__["a" /* UnsubscriptionError */]) { errors = errors.concat(flattenUnsubscriptionErrors(err.errors)); } else { errors.push(err); } } } } } if (hasErrors) { throw new __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__["a" /* UnsubscriptionError */](errors); } }; Subscription.prototype.add = function (teardown) { if (!teardown || (teardown === Subscription.EMPTY)) { return Subscription.EMPTY; } if (teardown === this) { return this; } var subscription = teardown; switch (typeof teardown) { case 'function': subscription = new Subscription(teardown); case 'object': if (subscription.closed || typeof subscription.unsubscribe !== 'function') { return subscription; } else if (this.closed) { subscription.unsubscribe(); return subscription; } else if (typeof subscription._addParent !== 'function') { var tmp = subscription; subscription = new Subscription(); subscription._subscriptions = [tmp]; } break; default: throw new Error('unrecognized teardown ' + teardown + ' added to Subscription.'); } var subscriptions = this._subscriptions || (this._subscriptions = []); subscriptions.push(subscription); subscription._addParent(this); return subscription; }; Subscription.prototype.remove = function (subscription) { var subscriptions = this._subscriptions; if (subscriptions) { var subscriptionIndex = subscriptions.indexOf(subscription); if (subscriptionIndex !== -1) { subscriptions.splice(subscriptionIndex, 1); } } }; Subscription.prototype._addParent = function (parent) { var _a = this, _parent = _a._parent, _parents = _a._parents; if (!_parent || _parent === parent) { this._parent = parent; } else if (!_parents) { this._parents = [parent]; } else if (_parents.indexOf(parent) === -1) { _parents.push(parent); } }; Subscription.EMPTY = (function (empty) { empty.closed = true; return empty; }(new Subscription())); return Subscription; }()); function flattenUnsubscriptionErrors(errors) { return errors.reduce(function (errs, err) { return errs.concat((err instanceof __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__["a" /* UnsubscriptionError */]) ? err.errors : err); }, []); } //# sourceMappingURL=Subscription.js.map /***/ }), /* 26 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2015 Joyent, Inc. module.exports = { bufferSplit: bufferSplit, addRSAMissing: addRSAMissing, calculateDSAPublic: calculateDSAPublic, calculateED25519Public: calculateED25519Public, calculateX25519Public: calculateX25519Public, mpNormalize: mpNormalize, mpDenormalize: mpDenormalize, ecNormalize: ecNormalize, countZeros: countZeros, assertCompatible: assertCompatible, isCompatible: isCompatible, opensslKeyDeriv: opensslKeyDeriv, opensshCipherInfo: opensshCipherInfo, publicFromPrivateECDSA: publicFromPrivateECDSA, zeroPadToLength: zeroPadToLength, writeBitString: writeBitString, readBitString: readBitString }; var assert = __webpack_require__(16); var Buffer = __webpack_require__(15).Buffer; var PrivateKey = __webpack_require__(33); var Key = __webpack_require__(27); var crypto = __webpack_require__(11); var algs = __webpack_require__(32); var asn1 = __webpack_require__(66); var ec, jsbn; var nacl; var MAX_CLASS_DEPTH = 3; function isCompatible(obj, klass, needVer) { if (obj === null || typeof (obj) !== 'object') return (false); if (needVer === undefined) needVer = klass.prototype._sshpkApiVersion; if (obj instanceof klass && klass.prototype._sshpkApiVersion[0] == needVer[0]) return (true); var proto = Object.getPrototypeOf(obj); var depth = 0; while (proto.constructor.name !== klass.name) { proto = Object.getPrototypeOf(proto); if (!proto || ++depth > MAX_CLASS_DEPTH) return (false); } if (proto.constructor.name !== klass.name) return (false); var ver = proto._sshpkApiVersion; if (ver === undefined) ver = klass._oldVersionDetect(obj); if (ver[0] != needVer[0] || ver[1] < needVer[1]) return (false); return (true); } function assertCompatible(obj, klass, needVer, name) { if (name === undefined) name = 'object'; assert.ok(obj, name + ' must not be null'); assert.object(obj, name + ' must be an object'); if (needVer === undefined) needVer = klass.prototype._sshpkApiVersion; if (obj instanceof klass && klass.prototype._sshpkApiVersion[0] == needVer[0]) return; var proto = Object.getPrototypeOf(obj); var depth = 0; while (proto.constructor.name !== klass.name) { proto = Object.getPrototypeOf(proto); assert.ok(proto && ++depth <= MAX_CLASS_DEPTH, name + ' must be a ' + klass.name + ' instance'); } assert.strictEqual(proto.constructor.name, klass.name, name + ' must be a ' + klass.name + ' instance'); var ver = proto._sshpkApiVersion; if (ver === undefined) ver = klass._oldVersionDetect(obj); assert.ok(ver[0] == needVer[0] && ver[1] >= needVer[1], name + ' must be compatible with ' + klass.name + ' klass ' + 'version ' + needVer[0] + '.' + needVer[1]); } var CIPHER_LEN = { 'des-ede3-cbc': { key: 7, iv: 8 }, 'aes-128-cbc': { key: 16, iv: 16 } }; var PKCS5_SALT_LEN = 8; function opensslKeyDeriv(cipher, salt, passphrase, count) { assert.buffer(salt, 'salt'); assert.buffer(passphrase, 'passphrase'); assert.number(count, 'iteration count'); var clen = CIPHER_LEN[cipher]; assert.object(clen, 'supported cipher'); salt = salt.slice(0, PKCS5_SALT_LEN); var D, D_prev, bufs; var material = Buffer.alloc(0); while (material.length < clen.key + clen.iv) { bufs = []; if (D_prev) bufs.push(D_prev); bufs.push(passphrase); bufs.push(salt); D = Buffer.concat(bufs); for (var j = 0; j < count; ++j) D = crypto.createHash('md5').update(D).digest(); material = Buffer.concat([material, D]); D_prev = D; } return ({ key: material.slice(0, clen.key), iv: material.slice(clen.key, clen.key + clen.iv) }); } /* Count leading zero bits on a buffer */ function countZeros(buf) { var o = 0, obit = 8; while (o < buf.length) { var mask = (1 << obit); if ((buf[o] & mask) === mask) break; obit--; if (obit < 0) { o++; obit = 8; } } return (o*8 + (8 - obit) - 1); } function bufferSplit(buf, chr) { assert.buffer(buf); assert.string(chr); var parts = []; var lastPart = 0; var matches = 0; for (var i = 0; i < buf.length; ++i) { if (buf[i] === chr.charCodeAt(matches)) ++matches; else if (buf[i] === chr.charCodeAt(0)) matches = 1; else matches = 0; if (matches >= chr.length) { var newPart = i + 1; parts.push(buf.slice(lastPart, newPart - matches)); lastPart = newPart; matches = 0; } } if (lastPart <= buf.length) parts.push(buf.slice(lastPart, buf.length)); return (parts); } function ecNormalize(buf, addZero) { assert.buffer(buf); if (buf[0] === 0x00 && buf[1] === 0x04) { if (addZero) return (buf); return (buf.slice(1)); } else if (buf[0] === 0x04) { if (!addZero) return (buf); } else { while (buf[0] === 0x00) buf = buf.slice(1); if (buf[0] === 0x02 || buf[0] === 0x03) throw (new Error('Compressed elliptic curve points ' + 'are not supported')); if (buf[0] !== 0x04) throw (new Error('Not a valid elliptic curve point')); if (!addZero) return (buf); } var b = Buffer.alloc(buf.length + 1); b[0] = 0x0; buf.copy(b, 1); return (b); } function readBitString(der, tag) { if (tag === undefined) tag = asn1.Ber.BitString; var buf = der.readString(tag, true); assert.strictEqual(buf[0], 0x00, 'bit strings with unused bits are ' + 'not supported (0x' + buf[0].toString(16) + ')'); return (buf.slice(1)); } function writeBitString(der, buf, tag) { if (tag === undefined) tag = asn1.Ber.BitString; var b = Buffer.alloc(buf.length + 1); b[0] = 0x00; buf.copy(b, 1); der.writeBuffer(b, tag); } function mpNormalize(buf) { assert.buffer(buf); while (buf.length > 1 && buf[0] === 0x00 && (buf[1] & 0x80) === 0x00) buf = buf.slice(1); if ((buf[0] & 0x80) === 0x80) { var b = Buffer.alloc(buf.length + 1); b[0] = 0x00; buf.copy(b, 1); buf = b; } return (buf); } function mpDenormalize(buf) { assert.buffer(buf); while (buf.length > 1 && buf[0] === 0x00) buf = buf.slice(1); return (buf); } function zeroPadToLength(buf, len) { assert.buffer(buf); assert.number(len); while (buf.length > len) { assert.equal(buf[0], 0x00); buf = buf.slice(1); } while (buf.length < len) { var b = Buffer.alloc(buf.length + 1); b[0] = 0x00; buf.copy(b, 1); buf = b; } return (buf); } function bigintToMpBuf(bigint) { var buf = Buffer.from(bigint.toByteArray()); buf = mpNormalize(buf); return (buf); } function calculateDSAPublic(g, p, x) { assert.buffer(g); assert.buffer(p); assert.buffer(x); try { var bigInt = __webpack_require__(81).BigInteger; } catch (e) { throw (new Error('To load a PKCS#8 format DSA private key, ' + 'the node jsbn library is required.')); } g = new bigInt(g); p = new bigInt(p); x = new bigInt(x); var y = g.modPow(x, p); var ybuf = bigintToMpBuf(y); return (ybuf); } function calculateED25519Public(k) { assert.buffer(k); if (nacl === undefined) nacl = __webpack_require__(76); var kp = nacl.sign.keyPair.fromSeed(new Uint8Array(k)); return (Buffer.from(kp.publicKey)); } function calculateX25519Public(k) { assert.buffer(k); if (nacl === undefined) nacl = __webpack_require__(76); var kp = nacl.box.keyPair.fromSeed(new Uint8Array(k)); return (Buffer.from(kp.publicKey)); } function addRSAMissing(key) { assert.object(key); assertCompatible(key, PrivateKey, [1, 1]); try { var bigInt = __webpack_require__(81).BigInteger; } catch (e) { throw (new Error('To write a PEM private key from ' + 'this source, the node jsbn lib is required.')); } var d = new bigInt(key.part.d.data); var buf; if (!key.part.dmodp) { var p = new bigInt(key.part.p.data); var dmodp = d.mod(p.subtract(1)); buf = bigintToMpBuf(dmodp); key.part.dmodp = {name: 'dmodp', data: buf}; key.parts.push(key.part.dmodp); } if (!key.part.dmodq) { var q = new bigInt(key.part.q.data); var dmodq = d.mod(q.subtract(1)); buf = bigintToMpBuf(dmodq); key.part.dmodq = {name: 'dmodq', data: buf}; key.parts.push(key.part.dmodq); } } function publicFromPrivateECDSA(curveName, priv) { assert.string(curveName, 'curveName'); assert.buffer(priv); if (ec === undefined) ec = __webpack_require__(139); if (jsbn === undefined) jsbn = __webpack_require__(81).BigInteger; var params = algs.curves[curveName]; var p = new jsbn(params.p); var a = new jsbn(params.a); var b = new jsbn(params.b); var curve = new ec.ECCurveFp(p, a, b); var G = curve.decodePointHex(params.G.toString('hex')); var d = new jsbn(mpNormalize(priv)); var pub = G.multiply(d); pub = Buffer.from(curve.encodePointHex(pub), 'hex'); var parts = []; parts.push({name: 'curve', data: Buffer.from(curveName)}); parts.push({name: 'Q', data: pub}); var key = new Key({type: 'ecdsa', curve: curve, parts: parts}); return (key); } function opensshCipherInfo(cipher) { var inf = {}; switch (cipher) { case '3des-cbc': inf.keySize = 24; inf.blockSize = 8; inf.opensslName = 'des-ede3-cbc'; break; case 'blowfish-cbc': inf.keySize = 16; inf.blockSize = 8; inf.opensslName = 'bf-cbc'; break; case 'aes128-cbc': case 'aes128-ctr': case 'aes128-gcm@openssh.com': inf.keySize = 16; inf.blockSize = 16; inf.opensslName = 'aes-128-' + cipher.slice(7, 10); break; case 'aes192-cbc': case 'aes192-ctr': case 'aes192-gcm@openssh.com': inf.keySize = 24; inf.blockSize = 16; inf.opensslName = 'aes-192-' + cipher.slice(7, 10); break; case 'aes256-cbc': case 'aes256-ctr': case 'aes256-gcm@openssh.com': inf.keySize = 32; inf.blockSize = 16; inf.opensslName = 'aes-256-' + cipher.slice(7, 10); break; default: throw (new Error( 'Unsupported openssl cipher "' + cipher + '"')); } return (inf); } /***/ }), /* 27 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Joyent, Inc. module.exports = Key; var assert = __webpack_require__(16); var algs = __webpack_require__(32); var crypto = __webpack_require__(11); var Fingerprint = __webpack_require__(156); var Signature = __webpack_require__(75); var DiffieHellman = __webpack_require__(325).DiffieHellman; var errs = __webpack_require__(74); var utils = __webpack_require__(26); var PrivateKey = __webpack_require__(33); var edCompat; try { edCompat = __webpack_require__(454); } catch (e) { /* Just continue through, and bail out if we try to use it. */ } var InvalidAlgorithmError = errs.InvalidAlgorithmError; var KeyParseError = errs.KeyParseError; var formats = {}; formats['auto'] = __webpack_require__(455); formats['pem'] = __webpack_require__(86); formats['pkcs1'] = __webpack_require__(327); formats['pkcs8'] = __webpack_require__(157); formats['rfc4253'] = __webpack_require__(103); formats['ssh'] = __webpack_require__(456); formats['ssh-private'] = __webpack_require__(193); formats['openssh'] = formats['ssh-private']; formats['dnssec'] = __webpack_require__(326); function Key(opts) { assert.object(opts, 'options'); assert.arrayOfObject(opts.parts, 'options.parts'); assert.string(opts.type, 'options.type'); assert.optionalString(opts.comment, 'options.comment'); var algInfo = algs.info[opts.type]; if (typeof (algInfo) !== 'object') throw (new InvalidAlgorithmError(opts.type)); var partLookup = {}; for (var i = 0; i < opts.parts.length; ++i) { var part = opts.parts[i]; partLookup[part.name] = part; } this.type = opts.type; this.parts = opts.parts; this.part = partLookup; this.comment = undefined; this.source = opts.source; /* for speeding up hashing/fingerprint operations */ this._rfc4253Cache = opts._rfc4253Cache; this._hashCache = {}; var sz; this.curve = undefined; if (this.type === 'ecdsa') { var curve = this.part.curve.data.toString(); this.curve = curve; sz = algs.curves[curve].size; } else if (this.type === 'ed25519' || this.type === 'curve25519') { sz = 256; this.curve = 'curve25519'; } else { var szPart = this.part[algInfo.sizePart]; sz = szPart.data.length; sz = sz * 8 - utils.countZeros(szPart.data); } this.size = sz; } Key.formats = formats; Key.prototype.toBuffer = function (format, options) { if (format === undefined) format = 'ssh'; assert.string(format, 'format'); assert.object(formats[format], 'formats[format]'); assert.optionalObject(options, 'options'); if (format === 'rfc4253') { if (this._rfc4253Cache === undefined) this._rfc4253Cache = formats['rfc4253'].write(this); return (this._rfc4253Cache); } return (formats[format].write(this, options)); }; Key.prototype.toString = function (format, options) { return (this.toBuffer(format, options).toString()); }; Key.prototype.hash = function (algo) { assert.string(algo, 'algorithm'); algo = algo.toLowerCase(); if (algs.hashAlgs[algo] === undefined) throw (new InvalidAlgorithmError(algo)); if (this._hashCache[algo]) return (this._hashCache[algo]); var hash = crypto.createHash(algo). update(this.toBuffer('rfc4253')).digest(); this._hashCache[algo] = hash; return (hash); }; Key.prototype.fingerprint = function (algo) { if (algo === undefined) algo = 'sha256'; assert.string(algo, 'algorithm'); var opts = { type: 'key', hash: this.hash(algo), algorithm: algo }; return (new Fingerprint(opts)); }; Key.prototype.defaultHashAlgorithm = function () { var hashAlgo = 'sha1'; if (this.type === 'rsa') hashAlgo = 'sha256'; if (this.type === 'dsa' && this.size > 1024) hashAlgo = 'sha256'; if (this.type === 'ed25519') hashAlgo = 'sha512'; if (this.type === 'ecdsa') { if (this.size <= 256) hashAlgo = 'sha256'; else if (this.size <= 384) hashAlgo = 'sha384'; else hashAlgo = 'sha512'; } return (hashAlgo); }; Key.prototype.createVerify = function (hashAlgo) { if (hashAlgo === undefined) hashAlgo = this.defaultHashAlgorithm(); assert.string(hashAlgo, 'hash algorithm'); /* ED25519 is not supported by OpenSSL, use a javascript impl. */ if (this.type === 'ed25519' && edCompat !== undefined) return (new edCompat.Verifier(this, hashAlgo)); if (this.type === 'curve25519') throw (new Error('Curve25519 keys are not suitable for ' + 'signing or verification')); var v, nm, err; try { nm = hashAlgo.toUpperCase(); v = crypto.createVerify(nm); } catch (e) { err = e; } if (v === undefined || (err instanceof Error && err.message.match(/Unknown message digest/))) { nm = 'RSA-'; nm += hashAlgo.toUpperCase(); v = crypto.createVerify(nm); } assert.ok(v, 'failed to create verifier'); var oldVerify = v.verify.bind(v); var key = this.toBuffer('pkcs8'); var curve = this.curve; var self = this; v.verify = function (signature, fmt) { if (Signature.isSignature(signature, [2, 0])) { if (signature.type !== self.type) return (false); if (signature.hashAlgorithm && signature.hashAlgorithm !== hashAlgo) return (false); if (signature.curve && self.type === 'ecdsa' && signature.curve !== curve) return (false); return (oldVerify(key, signature.toBuffer('asn1'))); } else if (typeof (signature) === 'string' || Buffer.isBuffer(signature)) { return (oldVerify(key, signature, fmt)); /* * Avoid doing this on valid arguments, walking the prototype * chain can be quite slow. */ } else if (Signature.isSignature(signature, [1, 0])) { throw (new Error('signature was created by too old ' + 'a version of sshpk and cannot be verified')); } else { throw (new TypeError('signature must be a string, ' + 'Buffer, or Signature object')); } }; return (v); }; Key.prototype.createDiffieHellman = function () { if (this.type === 'rsa') throw (new Error('RSA keys do not support Diffie-Hellman')); return (new DiffieHellman(this)); }; Key.prototype.createDH = Key.prototype.createDiffieHellman; Key.parse = function (data, format, options) { if (typeof (data) !== 'string') assert.buffer(data, 'data'); if (format === undefined) format = 'auto'; assert.string(format, 'format'); if (typeof (options) === 'string') options = { filename: options }; assert.optionalObject(options, 'options'); if (options === undefined) options = {}; assert.optionalString(options.filename, 'options.filename'); if (options.filename === undefined) options.filename = '(unnamed)'; assert.object(formats[format], 'formats[format]'); try { var k = formats[format].read(data, options); if (k instanceof PrivateKey) k = k.toPublic(); if (!k.comment) k.comment = options.filename; return (k); } catch (e) { if (e.name === 'KeyEncryptedError') throw (e); throw (new KeyParseError(options.filename, format, e)); } }; Key.isKey = function (obj, ver) { return (utils.isCompatible(obj, Key, ver)); }; /* * API versions for Key: * [1,0] -- initial ver, may take Signature for createVerify or may not * [1,1] -- added pkcs1, pkcs8 formats * [1,2] -- added auto, ssh-private, openssh formats * [1,3] -- added defaultHashAlgorithm * [1,4] -- added ed support, createDH * [1,5] -- first explicitly tagged version * [1,6] -- changed ed25519 part names */ Key.prototype._sshpkApiVersion = [1, 6]; Key._oldVersionDetect = function (obj) { assert.func(obj.toBuffer); assert.func(obj.fingerprint); if (obj.createDH) return ([1, 4]); if (obj.defaultHashAlgorithm) return ([1, 3]); if (obj.formats['auto']) return ([1, 2]); if (obj.formats['pkcs1']) return ([1, 1]); return ([1, 0]); }; /***/ }), /* 28 */ /***/ (function(module, exports) { module.exports = require("assert"); /***/ }), /* 29 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = nullify; function nullify(obj = {}) { if (Array.isArray(obj)) { for (var _iterator = obj, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var _ref; if (_isArray) { if (_i >= _iterator.length) break; _ref = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) break; _ref = _i.value; } const item = _ref; nullify(item); } } else if (obj !== null && typeof obj === 'object' || typeof obj === 'function') { Object.setPrototypeOf(obj, null); // for..in can only be applied to 'object', not 'function' if (typeof obj === 'object') { for (const key in obj) { nullify(obj[key]); } } } return obj; } /***/ }), /* 30 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const escapeStringRegexp = __webpack_require__(388); const ansiStyles = __webpack_require__(506); const stdoutColor = __webpack_require__(598).stdout; const template = __webpack_require__(599); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); // `supportsColor.level` → `ansiStyles.color[name]` mapping const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; // `color-convert` models to exclude from the Chalk API due to conflicts and such const skipModels = new Set(['gray']); const styles = Object.create(null); function applyOptions(obj, options) { options = options || {}; // Detect level if not set manually const scLevel = stdoutColor ? stdoutColor.level : 0; obj.level = options.level === undefined ? scLevel : options.level; obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0; } function Chalk(options) { // We check for this.template here since calling `chalk.constructor()` // by itself will have a `this` of a previously constructed chalk object if (!this || !(this instanceof Chalk) || this.template) { const chalk = {}; applyOptions(chalk, options); chalk.template = function () { const args = [].slice.call(arguments); return chalkTag.apply(null, [chalk.template].concat(args)); }; Object.setPrototypeOf(chalk, Chalk.prototype); Object.setPrototypeOf(chalk.template, chalk); chalk.template.constructor = Chalk; return chalk.template; } applyOptions(this, options); } // Use bright blue on Windows as the normal blue color is illegible if (isSimpleWindowsTerm) { ansiStyles.blue.open = '\u001B[94m'; } for (const key of Object.keys(ansiStyles)) { ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); styles[key] = { get() { const codes = ansiStyles[key]; return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); } }; } styles.visible = { get() { return build.call(this, this._styles || [], true, 'visible'); } }; ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); for (const model of Object.keys(ansiStyles.color.ansi)) { if (skipModels.has(model)) { continue; } styles[model] = { get() { const level = this.level; return function () { const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments); const codes = { open, close: ansiStyles.color.close, closeRe: ansiStyles.color.closeRe }; return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); }; } }; } ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); for (const model of Object.keys(ansiStyles.bgColor.ansi)) { if (skipModels.has(model)) { continue; } const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); styles[bgModel] = { get() { const level = this.level; return function () { const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments); const codes = { open, close: ansiStyles.bgColor.close, closeRe: ansiStyles.bgColor.closeRe }; return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); }; } }; } const proto = Object.defineProperties(() => {}, styles); function build(_styles, _empty, key) { const builder = function () { return applyStyle.apply(builder, arguments); }; builder._styles = _styles; builder._empty = _empty; const self = this; Object.defineProperty(builder, 'level', { enumerable: true, get() { return self.level; }, set(level) { self.level = level; } }); Object.defineProperty(builder, 'enabled', { enumerable: true, get() { return self.enabled; }, set(enabled) { self.enabled = enabled; } }); // See below for fix regarding invisible grey/dim combination on Windows builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; // `__proto__` is used because we must return a function, but there is // no way to create a function with a different prototype builder.__proto__ = proto; // eslint-disable-line no-proto return builder; } function applyStyle() { // Support varags, but simply cast to string in case there's only one arg const args = arguments; const argsLen = args.length; let str = String(arguments[0]); if (argsLen === 0) { return ''; } if (argsLen > 1) { // Don't slice `arguments`, it prevents V8 optimizations for (let a = 1; a < argsLen; a++) { str += ' ' + args[a]; } } if (!this.enabled || this.level <= 0 || !str) { return this._empty ? '' : str; } // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe, // see https://github.com/chalk/chalk/issues/58 // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop. const originalDim = ansiStyles.dim.open; if (isSimpleWindowsTerm && this.hasGrey) { ansiStyles.dim.open = ''; } for (const code of this._styles.slice().reverse()) { // Replace any instances already present with a re-opening code // otherwise only the part of the string until said closing code // will be colored, and the rest will simply be 'plain'. str = code.open + str.replace(code.closeRe, code.open) + code.close; // Close the styling before a linebreak and reopen // after next line to fix a bleed issue on macOS // https://github.com/chalk/chalk/pull/92 str = str.replace(/\r?\n/g, `${code.close}$&${code.open}`); } // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue ansiStyles.dim.open = originalDim; return str; } function chalkTag(chalk, strings) { if (!Array.isArray(strings)) { // If chalk() was called by itself or with a string, // return the string itself as a string. return [].slice.call(arguments, 1).join(' '); } const args = [].slice.call(arguments, 2); const parts = [strings.raw[0]]; for (let i = 1; i < strings.length; i++) { parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); parts.push(String(strings.raw[i])); } return template(chalk, parts.join('')); } Object.defineProperties(Chalk.prototype, styles); module.exports = Chalk(); // eslint-disable-line new-cap module.exports.supportsColor = stdoutColor; module.exports.default = module.exports; // For TypeScript /***/ }), /* 31 */ /***/ (function(module, exports) { var core = module.exports = { version: '2.5.7' }; if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef /***/ }), /* 32 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2015 Joyent, Inc. var Buffer = __webpack_require__(15).Buffer; var algInfo = { 'dsa': { parts: ['p', 'q', 'g', 'y'], sizePart: 'p' }, 'rsa': { parts: ['e', 'n'], sizePart: 'n' }, 'ecdsa': { parts: ['curve', 'Q'], sizePart: 'Q' }, 'ed25519': { parts: ['A'], sizePart: 'A' } }; algInfo['curve25519'] = algInfo['ed25519']; var algPrivInfo = { 'dsa': { parts: ['p', 'q', 'g', 'y', 'x'] }, 'rsa': { parts: ['n', 'e', 'd', 'iqmp', 'p', 'q'] }, 'ecdsa': { parts: ['curve', 'Q', 'd'] }, 'ed25519': { parts: ['A', 'k'] } }; algPrivInfo['curve25519'] = algPrivInfo['ed25519']; var hashAlgs = { 'md5': true, 'sha1': true, 'sha256': true, 'sha384': true, 'sha512': true }; /* * Taken from * http://csrc.nist.gov/groups/ST/toolkit/documents/dss/NISTReCur.pdf */ var curves = { 'nistp256': { size: 256, pkcs8oid: '1.2.840.10045.3.1.7', p: Buffer.from(('00' + 'ffffffff 00000001 00000000 00000000' + '00000000 ffffffff ffffffff ffffffff'). replace(/ /g, ''), 'hex'), a: Buffer.from(('00' + 'FFFFFFFF 00000001 00000000 00000000' + '00000000 FFFFFFFF FFFFFFFF FFFFFFFC'). replace(/ /g, ''), 'hex'), b: Buffer.from(( '5ac635d8 aa3a93e7 b3ebbd55 769886bc' + '651d06b0 cc53b0f6 3bce3c3e 27d2604b'). replace(/ /g, ''), 'hex'), s: Buffer.from(('00' + 'c49d3608 86e70493 6a6678e1 139d26b7' + '819f7e90'). replace(/ /g, ''), 'hex'), n: Buffer.from(('00' + 'ffffffff 00000000 ffffffff ffffffff' + 'bce6faad a7179e84 f3b9cac2 fc632551'). replace(/ /g, ''), 'hex'), G: Buffer.from(('04' + '6b17d1f2 e12c4247 f8bce6e5 63a440f2' + '77037d81 2deb33a0 f4a13945 d898c296' + '4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16' + '2bce3357 6b315ece cbb64068 37bf51f5'). replace(/ /g, ''), 'hex') }, 'nistp384': { size: 384, pkcs8oid: '1.3.132.0.34', p: Buffer.from(('00' + 'ffffffff ffffffff ffffffff ffffffff' + 'ffffffff ffffffff ffffffff fffffffe' + 'ffffffff 00000000 00000000 ffffffff'). replace(/ /g, ''), 'hex'), a: Buffer.from(('00' + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE' + 'FFFFFFFF 00000000 00000000 FFFFFFFC'). replace(/ /g, ''), 'hex'), b: Buffer.from(( 'b3312fa7 e23ee7e4 988e056b e3f82d19' + '181d9c6e fe814112 0314088f 5013875a' + 'c656398d 8a2ed19d 2a85c8ed d3ec2aef'). replace(/ /g, ''), 'hex'), s: Buffer.from(('00' + 'a335926a a319a27a 1d00896a 6773a482' + '7acdac73'). replace(/ /g, ''), 'hex'), n: Buffer.from(('00' + 'ffffffff ffffffff ffffffff ffffffff' + 'ffffffff ffffffff c7634d81 f4372ddf' + '581a0db2 48b0a77a ecec196a ccc52973'). replace(/ /g, ''), 'hex'), G: Buffer.from(('04' + 'aa87ca22 be8b0537 8eb1c71e f320ad74' + '6e1d3b62 8ba79b98 59f741e0 82542a38' + '5502f25d bf55296c 3a545e38 72760ab7' + '3617de4a 96262c6f 5d9e98bf 9292dc29' + 'f8f41dbd 289a147c e9da3113 b5f0b8c0' + '0a60b1ce 1d7e819d 7a431d7c 90ea0e5f'). replace(/ /g, ''), 'hex') }, 'nistp521': { size: 521, pkcs8oid: '1.3.132.0.35', p: Buffer.from(( '01ffffff ffffffff ffffffff ffffffff' + 'ffffffff ffffffff ffffffff ffffffff' + 'ffffffff ffffffff ffffffff ffffffff' + 'ffffffff ffffffff ffffffff ffffffff' + 'ffff').replace(/ /g, ''), 'hex'), a: Buffer.from(('01FF' + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFC'). replace(/ /g, ''), 'hex'), b: Buffer.from(('51' + '953eb961 8e1c9a1f 929a21a0 b68540ee' + 'a2da725b 99b315f3 b8b48991 8ef109e1' + '56193951 ec7e937b 1652c0bd 3bb1bf07' + '3573df88 3d2c34f1 ef451fd4 6b503f00'). replace(/ /g, ''), 'hex'), s: Buffer.from(('00' + 'd09e8800 291cb853 96cc6717 393284aa' + 'a0da64ba').replace(/ /g, ''), 'hex'), n: Buffer.from(('01ff' + 'ffffffff ffffffff ffffffff ffffffff' + 'ffffffff ffffffff ffffffff fffffffa' + '51868783 bf2f966b 7fcc0148 f709a5d0' + '3bb5c9b8 899c47ae bb6fb71e 91386409'). replace(/ /g, ''), 'hex'), G: Buffer.from(('04' + '00c6 858e06b7 0404e9cd 9e3ecb66 2395b442' + '9c648139 053fb521 f828af60 6b4d3dba' + 'a14b5e77 efe75928 fe1dc127 a2ffa8de' + '3348b3c1 856a429b f97e7e31 c2e5bd66' + '0118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9' + '98f54449 579b4468 17afbd17 273e662c' + '97ee7299 5ef42640 c550b901 3fad0761' + '353c7086 a272c240 88be9476 9fd16650'). replace(/ /g, ''), 'hex') } }; module.exports = { info: algInfo, privInfo: algPrivInfo, hashAlgs: hashAlgs, curves: curves }; /***/ }), /* 33 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Joyent, Inc. module.exports = PrivateKey; var assert = __webpack_require__(16); var Buffer = __webpack_require__(15).Buffer; var algs = __webpack_require__(32); var crypto = __webpack_require__(11); var Fingerprint = __webpack_require__(156); var Signature = __webpack_require__(75); var errs = __webpack_require__(74); var util = __webpack_require__(3); var utils = __webpack_require__(26); var dhe = __webpack_require__(325); var generateECDSA = dhe.generateECDSA; var generateED25519 = dhe.generateED25519; var edCompat; var nacl; try { edCompat = __webpack_require__(454); } catch (e) { /* Just continue through, and bail out if we try to use it. */ } var Key = __webpack_require__(27); var InvalidAlgorithmError = errs.InvalidAlgorithmError; var KeyParseError = errs.KeyParseError; var KeyEncryptedError = errs.KeyEncryptedError; var formats = {}; formats['auto'] = __webpack_require__(455); formats['pem'] = __webpack_require__(86); formats['pkcs1'] = __webpack_require__(327); formats['pkcs8'] = __webpack_require__(157); formats['rfc4253'] = __webpack_require__(103); formats['ssh-private'] = __webpack_require__(193); formats['openssh'] = formats['ssh-private']; formats['ssh'] = formats['ssh-private']; formats['dnssec'] = __webpack_require__(326); function PrivateKey(opts) { assert.object(opts, 'options'); Key.call(this, opts); this._pubCache = undefined; } util.inherits(PrivateKey, Key); PrivateKey.formats = formats; PrivateKey.prototype.toBuffer = function (format, options) { if (format === undefined) format = 'pkcs1'; assert.string(format, 'format'); assert.object(formats[format], 'formats[format]'); assert.optionalObject(options, 'options'); return (formats[format].write(this, options)); }; PrivateKey.prototype.hash = function (algo) { return (this.toPublic().hash(algo)); }; PrivateKey.prototype.toPublic = function () { if (this._pubCache) return (this._pubCache); var algInfo = algs.info[this.type]; var pubParts = []; for (var i = 0; i < algInfo.parts.length; ++i) { var p = algInfo.parts[i]; pubParts.push(this.part[p]); } this._pubCache = new Key({ type: this.type, source: this, parts: pubParts }); if (this.comment) this._pubCache.comment = this.comment; return (this._pubCache); }; PrivateKey.prototype.derive = function (newType) { assert.string(newType, 'type'); var priv, pub, pair; if (this.type === 'ed25519' && newType === 'curve25519') { if (nacl === undefined) nacl = __webpack_require__(76); priv = this.part.k.data; if (priv[0] === 0x00) priv = priv.slice(1); pair = nacl.box.keyPair.fromSecretKey(new Uint8Array(priv)); pub = Buffer.from(pair.publicKey); return (new PrivateKey({ type: 'curve25519', parts: [ { name: 'A', data: utils.mpNormalize(pub) }, { name: 'k', data: utils.mpNormalize(priv) } ] })); } else if (this.type === 'curve25519' && newType === 'ed25519') { if (nacl === undefined) nacl = __webpack_require__(76); priv = this.part.k.data; if (priv[0] === 0x00) priv = priv.slice(1); pair = nacl.sign.keyPair.fromSeed(new Uint8Array(priv)); pub = Buffer.from(pair.publicKey); return (new PrivateKey({ type: 'ed25519', parts: [ { name: 'A', data: utils.mpNormalize(pub) }, { name: 'k', data: utils.mpNormalize(priv) } ] })); } throw (new Error('Key derivation not supported from ' + this.type + ' to ' + newType)); }; PrivateKey.prototype.createVerify = function (hashAlgo) { return (this.toPublic().createVerify(hashAlgo)); }; PrivateKey.prototype.createSign = function (hashAlgo) { if (hashAlgo === undefined) hashAlgo = this.defaultHashAlgorithm(); assert.string(hashAlgo, 'hash algorithm'); /* ED25519 is not supported by OpenSSL, use a javascript impl. */ if (this.type === 'ed25519' && edCompat !== undefined) return (new edCompat.Signer(this, hashAlgo)); if (this.type === 'curve25519') throw (new Error('Curve25519 keys are not suitable for ' + 'signing or verification')); var v, nm, err; try { nm = hashAlgo.toUpperCase(); v = crypto.createSign(nm); } catch (e) { err = e; } if (v === undefined || (err instanceof Error && err.message.match(/Unknown message digest/))) { nm = 'RSA-'; nm += hashAlgo.toUpperCase(); v = crypto.createSign(nm); } assert.ok(v, 'failed to create verifier'); var oldSign = v.sign.bind(v); var key = this.toBuffer('pkcs1'); var type = this.type; var curve = this.curve; v.sign = function () { var sig = oldSign(key); if (typeof (sig) === 'string') sig = Buffer.from(sig, 'binary'); sig = Signature.parse(sig, type, 'asn1'); sig.hashAlgorithm = hashAlgo; sig.curve = curve; return (sig); }; return (v); }; PrivateKey.parse = function (data, format, options) { if (typeof (data) !== 'string') assert.buffer(data, 'data'); if (format === undefined) format = 'auto'; assert.string(format, 'format'); if (typeof (options) === 'string') options = { filename: options }; assert.optionalObject(options, 'options'); if (options === undefined) options = {}; assert.optionalString(options.filename, 'options.filename'); if (options.filename === undefined) options.filename = '(unnamed)'; assert.object(formats[format], 'formats[format]'); try { var k = formats[format].read(data, options); assert.ok(k instanceof PrivateKey, 'key is not a private key'); if (!k.comment) k.comment = options.filename; return (k); } catch (e) { if (e.name === 'KeyEncryptedError') throw (e); throw (new KeyParseError(options.filename, format, e)); } }; PrivateKey.isPrivateKey = function (obj, ver) { return (utils.isCompatible(obj, PrivateKey, ver)); }; PrivateKey.generate = function (type, options) { if (options === undefined) options = {}; assert.object(options, 'options'); switch (type) { case 'ecdsa': if (options.curve === undefined) options.curve = 'nistp256'; assert.string(options.curve, 'options.curve'); return (generateECDSA(options.curve)); case 'ed25519': return (generateED25519()); default: throw (new Error('Key generation not supported with key ' + 'type "' + type + '"')); } }; /* * API versions for PrivateKey: * [1,0] -- initial ver * [1,1] -- added auto, pkcs[18], openssh/ssh-private formats * [1,2] -- added defaultHashAlgorithm * [1,3] -- added derive, ed, createDH * [1,4] -- first tagged version * [1,5] -- changed ed25519 part names and format */ PrivateKey.prototype._sshpkApiVersion = [1, 5]; PrivateKey._oldVersionDetect = function (obj) { assert.func(obj.toPublic); assert.func(obj.createSign); if (obj.derive) return ([1, 3]); if (obj.defaultHashAlgorithm) return ([1, 2]); if (obj.formats['auto']) return ([1, 1]); return ([1, 0]); }; /***/ }), /* 34 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.wrapLifecycle = exports.run = exports.install = exports.Install = undefined; var _extends2; function _load_extends() { return _extends2 = _interopRequireDefault(__webpack_require__(20)); } var _asyncToGenerator2; function _load_asyncToGenerator() { return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(2)); } let install = exports.install = (() => { var _ref29 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, reporter, flags, lockfile) { yield wrapLifecycle(config, flags, (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const install = new Install(flags, config, reporter, lockfile); yield install.init(); })); }); return function install(_x7, _x8, _x9, _x10) { return _ref29.apply(this, arguments); }; })(); let run = exports.run = (() => { var _ref31 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, reporter, flags, args) { let lockfile; let error = 'installCommandRenamed'; if (flags.lockfile === false) { lockfile = new (_lockfile || _load_lockfile()).default(); } else { lockfile = yield (_lockfile || _load_lockfile()).default.fromDirectory(config.lockfileFolder, reporter); } if (args.length) { const exampleArgs = args.slice(); if (flags.saveDev) { exampleArgs.push('--dev'); } if (flags.savePeer) { exampleArgs.push('--peer'); } if (flags.saveOptional) { exampleArgs.push('--optional'); } if (flags.saveExact) { exampleArgs.push('--exact'); } if (flags.saveTilde) { exampleArgs.push('--tilde'); } let command = 'add'; if (flags.global) { error = 'globalFlagRemoved'; command = 'global add'; } throw new (_errors || _load_errors()).MessageError(reporter.lang(error, `yarn ${command} ${exampleArgs.join(' ')}`)); } yield install(config, reporter, flags, lockfile); }); return function run(_x11, _x12, _x13, _x14) { return _ref31.apply(this, arguments); }; })(); let wrapLifecycle = exports.wrapLifecycle = (() => { var _ref32 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, flags, factory) { yield config.executeLifecycleScript('preinstall'); yield factory(); // npm behaviour, seems kinda funky but yay compatibility yield config.executeLifecycleScript('install'); yield config.executeLifecycleScript('postinstall'); if (!config.production) { if (!config.disablePrepublish) { yield config.executeLifecycleScript('prepublish'); } yield config.executeLifecycleScript('prepare'); } }); return function wrapLifecycle(_x15, _x16, _x17) { return _ref32.apply(this, arguments); }; })(); exports.hasWrapper = hasWrapper; exports.setFlags = setFlags; var _objectPath; function _load_objectPath() { return _objectPath = _interopRequireDefault(__webpack_require__(304)); } var _hooks; function _load_hooks() { return _hooks = __webpack_require__(374); } var _index; function _load_index() { return _index = _interopRequireDefault(__webpack_require__(221)); } var _errors; function _load_errors() { return _errors = __webpack_require__(6); } var _integrityChecker; function _load_integrityChecker() { return _integrityChecker = _interopRequireDefault(__webpack_require__(209)); } var _lockfile; function _load_lockfile() { return _lockfile = _interopRequireDefault(__webpack_require__(19)); } var _lockfile2; function _load_lockfile2() { return _lockfile2 = __webpack_require__(19); } var _packageFetcher; function _load_packageFetcher() { return _packageFetcher = _interopRequireWildcard(__webpack_require__(211)); } var _packageInstallScripts; function _load_packageInstallScripts() { return _packageInstallScripts = _interopRequireDefault(__webpack_require__(557)); } var _packageCompatibility; function _load_packageCompatibility() { return _packageCompatibility = _interopRequireWildcard(__webpack_require__(210)); } var _packageResolver; function _load_packageResolver() { return _packageResolver = _interopRequireDefault(__webpack_require__(366)); } var _packageLinker; function _load_packageLinker() { return _packageLinker = _interopRequireDefault(__webpack_require__(212)); } var _index2; function _load_index2() { return _index2 = __webpack_require__(58); } var _index3; function _load_index3() { return _index3 = __webpack_require__(78); } var _autoclean; function _load_autoclean() { return _autoclean = __webpack_require__(354); } var _constants; function _load_constants() { return _constants = _interopRequireWildcard(__webpack_require__(8)); } var _normalizePattern; function _load_normalizePattern() { return _normalizePattern = __webpack_require__(37); } var _fs; function _load_fs() { return _fs = _interopRequireWildcard(__webpack_require__(5)); } var _map; function _load_map() { return _map = _interopRequireDefault(__webpack_require__(29)); } var _yarnVersion; function _load_yarnVersion() { return _yarnVersion = __webpack_require__(105); } var _generatePnpMap; function _load_generatePnpMap() { return _generatePnpMap = __webpack_require__(579); } var _workspaceLayout; function _load_workspaceLayout() { return _workspaceLayout = _interopRequireDefault(__webpack_require__(90)); } var _resolutionMap; function _load_resolutionMap() { return _resolutionMap = _interopRequireDefault(__webpack_require__(215)); } var _guessName; function _load_guessName() { return _guessName = _interopRequireDefault(__webpack_require__(169)); } var _audit; function _load_audit() { return _audit = _interopRequireDefault(__webpack_require__(353)); } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const deepEqual = __webpack_require__(631); const emoji = __webpack_require__(302); const invariant = __webpack_require__(9); const path = __webpack_require__(0); const semver = __webpack_require__(22); const uuid = __webpack_require__(120); const ssri = __webpack_require__(65); const ONE_DAY = 1000 * 60 * 60 * 24; /** * Try and detect the installation method for Yarn and provide a command to update it with. */ function getUpdateCommand(installationMethod) { if (installationMethod === 'tar') { return `curl --compressed -o- -L ${(_constants || _load_constants()).YARN_INSTALLER_SH} | bash`; } if (installationMethod === 'homebrew') { return 'brew upgrade yarn'; } if (installationMethod === 'deb') { return 'sudo apt-get update && sudo apt-get install yarn'; } if (installationMethod === 'rpm') { return 'sudo yum install yarn'; } if (installationMethod === 'npm') { return 'npm install --global yarn'; } if (installationMethod === 'chocolatey') { return 'choco upgrade yarn'; } if (installationMethod === 'apk') { return 'apk update && apk add -u yarn'; } if (installationMethod === 'portage') { return 'sudo emerge --sync && sudo emerge -au sys-apps/yarn'; } return null; } function getUpdateInstaller(installationMethod) { // Windows if (installationMethod === 'msi') { return (_constants || _load_constants()).YARN_INSTALLER_MSI; } return null; } function normalizeFlags(config, rawFlags) { const flags = { // install har: !!rawFlags.har, ignorePlatform: !!rawFlags.ignorePlatform, ignoreEngines: !!rawFlags.ignoreEngines, ignoreScripts: !!rawFlags.ignoreScripts, ignoreOptional: !!rawFlags.ignoreOptional, force: !!rawFlags.force, flat: !!rawFlags.flat, lockfile: rawFlags.lockfile !== false, pureLockfile: !!rawFlags.pureLockfile, updateChecksums: !!rawFlags.updateChecksums, skipIntegrityCheck: !!rawFlags.skipIntegrityCheck, frozenLockfile: !!rawFlags.frozenLockfile, linkDuplicates: !!rawFlags.linkDuplicates, checkFiles: !!rawFlags.checkFiles, audit: !!rawFlags.audit, // add peer: !!rawFlags.peer, dev: !!rawFlags.dev, optional: !!rawFlags.optional, exact: !!rawFlags.exact, tilde: !!rawFlags.tilde, ignoreWorkspaceRootCheck: !!rawFlags.ignoreWorkspaceRootCheck, // outdated, update-interactive includeWorkspaceDeps: !!rawFlags.includeWorkspaceDeps, // add, remove, update workspaceRootIsCwd: rawFlags.workspaceRootIsCwd !== false }; if (config.getOption('ignore-scripts')) { flags.ignoreScripts = true; } if (config.getOption('ignore-platform')) { flags.ignorePlatform = true; } if (config.getOption('ignore-engines')) { flags.ignoreEngines = true; } if (config.getOption('ignore-optional')) { flags.ignoreOptional = true; } if (config.getOption('force')) { flags.force = true; } return flags; } class Install { constructor(flags, config, reporter, lockfile) { this.rootManifestRegistries = []; this.rootPatternsToOrigin = (0, (_map || _load_map()).default)(); this.lockfile = lockfile; this.reporter = reporter; this.config = config; this.flags = normalizeFlags(config, flags); this.resolutions = (0, (_map || _load_map()).default)(); // Legacy resolutions field used for flat install mode this.resolutionMap = new (_resolutionMap || _load_resolutionMap()).default(config); // Selective resolutions for nested dependencies this.resolver = new (_packageResolver || _load_packageResolver()).default(config, lockfile, this.resolutionMap); this.integrityChecker = new (_integrityChecker || _load_integrityChecker()).default(config); this.linker = new (_packageLinker || _load_packageLinker()).default(config, this.resolver); this.scripts = new (_packageInstallScripts || _load_packageInstallScripts()).default(config, this.resolver, this.flags.force); } /** * Create a list of dependency requests from the current directories manifests. */ fetchRequestFromCwd(excludePatterns = [], ignoreUnusedPatterns = false) { var _this = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const patterns = []; const deps = []; let resolutionDeps = []; const manifest = {}; const ignorePatterns = []; const usedPatterns = []; let workspaceLayout; // some commands should always run in the context of the entire workspace const cwd = _this.flags.includeWorkspaceDeps || _this.flags.workspaceRootIsCwd ? _this.config.lockfileFolder : _this.config.cwd; // non-workspaces are always root, otherwise check for workspace root const cwdIsRoot = !_this.config.workspaceRootFolder || _this.config.lockfileFolder === cwd; // exclude package names that are in install args const excludeNames = []; for (var _iterator = excludePatterns, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var _ref; if (_isArray) { if (_i >= _iterator.length) break; _ref = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) break; _ref = _i.value; } const pattern = _ref; if ((0, (_index3 || _load_index3()).getExoticResolver)(pattern)) { excludeNames.push((0, (_guessName || _load_guessName()).default)(pattern)); } else { // extract the name const parts = (0, (_normalizePattern || _load_normalizePattern()).normalizePattern)(pattern); excludeNames.push(parts.name); } } const stripExcluded = function stripExcluded(manifest) { for (var _iterator2 = excludeNames, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { var _ref2; if (_isArray2) { if (_i2 >= _iterator2.length) break; _ref2 = _iterator2[_i2++]; } else { _i2 = _iterator2.next(); if (_i2.done) break; _ref2 = _i2.value; } const exclude = _ref2; if (manifest.dependencies && manifest.dependencies[exclude]) { delete manifest.dependencies[exclude]; } if (manifest.devDependencies && manifest.devDependencies[exclude]) { delete manifest.devDependencies[exclude]; } if (manifest.optionalDependencies && manifest.optionalDependencies[exclude]) { delete manifest.optionalDependencies[exclude]; } } }; for (var _iterator3 = Object.keys((_index2 || _load_index2()).registries), _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { var _ref3; if (_isArray3) { if (_i3 >= _iterator3.length) break; _ref3 = _iterator3[_i3++]; } else { _i3 = _iterator3.next(); if (_i3.done) break; _ref3 = _i3.value; } const registry = _ref3; const filename = (_index2 || _load_index2()).registries[registry].filename; const loc = path.join(cwd, filename); if (!(yield (_fs || _load_fs()).exists(loc))) { continue; } _this.rootManifestRegistries.push(registry); const projectManifestJson = yield _this.config.readJson(loc); yield (0, (_index || _load_index()).default)(projectManifestJson, cwd, _this.config, cwdIsRoot); Object.assign(_this.resolutions, projectManifestJson.resolutions); Object.assign(manifest, projectManifestJson); _this.resolutionMap.init(_this.resolutions); for (var _iterator4 = Object.keys(_this.resolutionMap.resolutionsByPackage), _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { var _ref4; if (_isArray4) { if (_i4 >= _iterator4.length) break; _ref4 = _iterator4[_i4++]; } else { _i4 = _iterator4.next(); if (_i4.done) break; _ref4 = _i4.value; } const packageName = _ref4; const optional = (_objectPath || _load_objectPath()).default.has(manifest.optionalDependencies, packageName) && _this.flags.ignoreOptional; for (var _iterator8 = _this.resolutionMap.resolutionsByPackage[packageName], _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { var _ref9; if (_isArray8) { if (_i8 >= _iterator8.length) break; _ref9 = _iterator8[_i8++]; } else { _i8 = _iterator8.next(); if (_i8.done) break; _ref9 = _i8.value; } const _ref8 = _ref9; const pattern = _ref8.pattern; resolutionDeps = [...resolutionDeps, { registry, pattern, optional, hint: 'resolution' }]; } } const pushDeps = function pushDeps(depType, manifest, { hint, optional }, isUsed) { if (ignoreUnusedPatterns && !isUsed) { return; } // We only take unused dependencies into consideration to get deterministic hoisting. // Since flat mode doesn't care about hoisting and everything is top level and specified then we can safely // leave these out. if (_this.flags.flat && !isUsed) { return; } const depMap = manifest[depType]; for (const name in depMap) { if (excludeNames.indexOf(name) >= 0) { continue; } let pattern = name; if (!_this.lockfile.getLocked(pattern)) { // when we use --save we save the dependency to the lockfile with just the name rather than the // version combo pattern += '@' + depMap[name]; } // normalization made sure packages are mentioned only once if (isUsed) { usedPatterns.push(pattern); } else { ignorePatterns.push(pattern); } _this.rootPatternsToOrigin[pattern] = depType; patterns.push(pattern); deps.push({ pattern, registry, hint, optional, workspaceName: manifest.name, workspaceLoc: manifest._loc }); } }; if (cwdIsRoot) { pushDeps('dependencies', projectManifestJson, { hint: null, optional: false }, true); pushDeps('devDependencies', projectManifestJson, { hint: 'dev', optional: false }, !_this.config.production); pushDeps('optionalDependencies', projectManifestJson, { hint: 'optional', optional: true }, true); } if (_this.config.workspaceRootFolder) { const workspaceLoc = cwdIsRoot ? loc : path.join(_this.config.lockfileFolder, filename); const workspacesRoot = path.dirname(workspaceLoc); let workspaceManifestJson = projectManifestJson; if (!cwdIsRoot) { // the manifest we read before was a child workspace, so get the root workspaceManifestJson = yield _this.config.readJson(workspaceLoc); yield (0, (_index || _load_index()).default)(workspaceManifestJson, workspacesRoot, _this.config, true); } const workspaces = yield _this.config.resolveWorkspaces(workspacesRoot, workspaceManifestJson); workspaceLayout = new (_workspaceLayout || _load_workspaceLayout()).default(workspaces, _this.config); // add virtual manifest that depends on all workspaces, this way package hoisters and resolvers will work fine const workspaceDependencies = (0, (_extends2 || _load_extends()).default)({}, workspaceManifestJson.dependencies); for (var _iterator5 = Object.keys(workspaces), _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { var _ref5; if (_isArray5) { if (_i5 >= _iterator5.length) break; _ref5 = _iterator5[_i5++]; } else { _i5 = _iterator5.next(); if (_i5.done) break; _ref5 = _i5.value; } const workspaceName = _ref5; const workspaceManifest = workspaces[workspaceName].manifest; workspaceDependencies[workspaceName] = workspaceManifest.version; // include dependencies from all workspaces if (_this.flags.includeWorkspaceDeps) { pushDeps('dependencies', workspaceManifest, { hint: null, optional: false }, true); pushDeps('devDependencies', workspaceManifest, { hint: 'dev', optional: false }, !_this.config.production); pushDeps('optionalDependencies', workspaceManifest, { hint: 'optional', optional: true }, true); } } const virtualDependencyManifest = { _uid: '', name: `workspace-aggregator-${uuid.v4()}`, version: '1.0.0', _registry: 'npm', _loc: workspacesRoot, dependencies: workspaceDependencies, devDependencies: (0, (_extends2 || _load_extends()).default)({}, workspaceManifestJson.devDependencies), optionalDependencies: (0, (_extends2 || _load_extends()).default)({}, workspaceManifestJson.optionalDependencies), private: workspaceManifestJson.private, workspaces: workspaceManifestJson.workspaces }; workspaceLayout.virtualManifestName = virtualDependencyManifest.name; const virtualDep = {}; virtualDep[virtualDependencyManifest.name] = virtualDependencyManifest.version; workspaces[virtualDependencyManifest.name] = { loc: workspacesRoot, manifest: virtualDependencyManifest }; // ensure dependencies that should be excluded are stripped from the correct manifest stripExcluded(cwdIsRoot ? virtualDependencyManifest : workspaces[projectManifestJson.name].manifest); pushDeps('workspaces', { workspaces: virtualDep }, { hint: 'workspaces', optional: false }, true); const implicitWorkspaceDependencies = (0, (_extends2 || _load_extends()).default)({}, workspaceDependencies); for (var _iterator6 = (_constants || _load_constants()).OWNED_DEPENDENCY_TYPES, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { var _ref6; if (_isArray6) { if (_i6 >= _iterator6.length) break; _ref6 = _iterator6[_i6++]; } else { _i6 = _iterator6.next(); if (_i6.done) break; _ref6 = _i6.value; } const type = _ref6; for (var _iterator7 = Object.keys(projectManifestJson[type] || {}), _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { var _ref7; if (_isArray7) { if (_i7 >= _iterator7.length) break; _ref7 = _iterator7[_i7++]; } else { _i7 = _iterator7.next(); if (_i7.done) break; _ref7 = _i7.value; } const dependencyName = _ref7; delete implicitWorkspaceDependencies[dependencyName]; } } pushDeps('dependencies', { dependencies: implicitWorkspaceDependencies }, { hint: 'workspaces', optional: false }, true); } break; } // inherit root flat flag if (manifest.flat) { _this.flags.flat = true; } return { requests: [...resolutionDeps, ...deps], patterns, manifest, usedPatterns, ignorePatterns, workspaceLayout }; })(); } /** * TODO description */ prepareRequests(requests) { return requests; } preparePatterns(patterns) { return patterns; } preparePatternsForLinking(patterns, cwdManifest, cwdIsRoot) { return patterns; } prepareManifests() { var _this2 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const manifests = yield _this2.config.getRootManifests(); return manifests; })(); } bailout(patterns, workspaceLayout) { var _this3 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { // We don't want to skip the audit - it could yield important errors if (_this3.flags.audit) { return false; } // PNP is so fast that the integrity check isn't pertinent if (_this3.config.plugnplayEnabled) { return false; } if (_this3.flags.skipIntegrityCheck || _this3.flags.force) { return false; } const lockfileCache = _this3.lockfile.cache; if (!lockfileCache) { return false; } const lockfileClean = _this3.lockfile.parseResultType === 'success'; const match = yield _this3.integrityChecker.check(patterns, lockfileCache, _this3.flags, workspaceLayout); if (_this3.flags.frozenLockfile && (!lockfileClean || match.missingPatterns.length > 0)) { throw new (_errors || _load_errors()).MessageError(_this3.reporter.lang('frozenLockfileError')); } const haveLockfile = yield (_fs || _load_fs()).exists(path.join(_this3.config.lockfileFolder, (_constants || _load_constants()).LOCKFILE_FILENAME)); const lockfileIntegrityPresent = !_this3.lockfile.hasEntriesExistWithoutIntegrity(); const integrityBailout = lockfileIntegrityPresent || !_this3.config.autoAddIntegrity; if (match.integrityMatches && haveLockfile && lockfileClean && integrityBailout) { _this3.reporter.success(_this3.reporter.lang('upToDate')); return true; } if (match.integrityFileMissing && haveLockfile) { // Integrity file missing, force script installations _this3.scripts.setForce(true); return false; } if (match.hardRefreshRequired) { // e.g. node version doesn't match, force script installations _this3.scripts.setForce(true); return false; } if (!patterns.length && !match.integrityFileMissing) { _this3.reporter.success(_this3.reporter.lang('nothingToInstall')); yield _this3.createEmptyManifestFolders(); yield _this3.saveLockfileAndIntegrity(patterns, workspaceLayout); return true; } return false; })(); } /** * Produce empty folders for all used root manifests. */ createEmptyManifestFolders() { var _this4 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { if (_this4.config.modulesFolder) { // already created return; } for (var _iterator9 = _this4.rootManifestRegistries, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { var _ref10; if (_isArray9) { if (_i9 >= _iterator9.length) break; _ref10 = _iterator9[_i9++]; } else { _i9 = _iterator9.next(); if (_i9.done) break; _ref10 = _i9.value; } const registryName = _ref10; const folder = _this4.config.registries[registryName].folder; yield (_fs || _load_fs()).mkdirp(path.join(_this4.config.lockfileFolder, folder)); } })(); } /** * TODO description */ markIgnored(patterns) { for (var _iterator10 = patterns, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { var _ref11; if (_isArray10) { if (_i10 >= _iterator10.length) break; _ref11 = _iterator10[_i10++]; } else { _i10 = _iterator10.next(); if (_i10.done) break; _ref11 = _i10.value; } const pattern = _ref11; const manifest = this.resolver.getStrictResolvedPattern(pattern); const ref = manifest._reference; invariant(ref, 'expected package reference'); // just mark the package as ignored. if the package is used by a required package, the hoister // will take care of that. ref.ignore = true; } } /** * helper method that gets only recent manifests * used by global.ls command */ getFlattenedDeps() { var _this5 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { var _ref12 = yield _this5.fetchRequestFromCwd(); const depRequests = _ref12.requests, rawPatterns = _ref12.patterns; yield _this5.resolver.init(depRequests, {}); const manifests = yield (_packageFetcher || _load_packageFetcher()).fetch(_this5.resolver.getManifests(), _this5.config); _this5.resolver.updateManifests(manifests); return _this5.flatten(rawPatterns); })(); } /** * TODO description */ init() { var _this6 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { _this6.checkUpdate(); // warn if we have a shrinkwrap if (yield (_fs || _load_fs()).exists(path.join(_this6.config.lockfileFolder, (_constants || _load_constants()).NPM_SHRINKWRAP_FILENAME))) { _this6.reporter.warn(_this6.reporter.lang('shrinkwrapWarning')); } // warn if we have an npm lockfile if (yield (_fs || _load_fs()).exists(path.join(_this6.config.lockfileFolder, (_constants || _load_constants()).NPM_LOCK_FILENAME))) { _this6.reporter.warn(_this6.reporter.lang('npmLockfileWarning')); } if (_this6.config.plugnplayEnabled) { _this6.reporter.info(_this6.reporter.lang('plugnplaySuggestV2L1')); _this6.reporter.info(_this6.reporter.lang('plugnplaySuggestV2L2')); } let flattenedTopLevelPatterns = []; const steps = []; var _ref13 = yield _this6.fetchRequestFromCwd(); const depRequests = _ref13.requests, rawPatterns = _ref13.patterns, ignorePatterns = _ref13.ignorePatterns, workspaceLayout = _ref13.workspaceLayout, manifest = _ref13.manifest; let topLevelPatterns = []; const artifacts = yield _this6.integrityChecker.getArtifacts(); if (artifacts) { _this6.linker.setArtifacts(artifacts); _this6.scripts.setArtifacts(artifacts); } if ((_packageCompatibility || _load_packageCompatibility()).shouldCheck(manifest, _this6.flags)) { steps.push((() => { var _ref14 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (curr, total) { _this6.reporter.step(curr, total, _this6.reporter.lang('checkingManifest'), emoji.get('mag')); yield _this6.checkCompatibility(); }); return function (_x, _x2) { return _ref14.apply(this, arguments); }; })()); } const audit = new (_audit || _load_audit()).default(_this6.config, _this6.reporter, { groups: (_constants || _load_constants()).OWNED_DEPENDENCY_TYPES }); let auditFoundProblems = false; steps.push(function (curr, total) { return (0, (_hooks || _load_hooks()).callThroughHook)('resolveStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { _this6.reporter.step(curr, total, _this6.reporter.lang('resolvingPackages'), emoji.get('mag')); yield _this6.resolver.init(_this6.prepareRequests(depRequests), { isFlat: _this6.flags.flat, isFrozen: _this6.flags.frozenLockfile, workspaceLayout }); topLevelPatterns = _this6.preparePatterns(rawPatterns); flattenedTopLevelPatterns = yield _this6.flatten(topLevelPatterns); return { bailout: !_this6.flags.audit && (yield _this6.bailout(topLevelPatterns, workspaceLayout)) }; })); }); if (_this6.flags.audit) { steps.push(function (curr, total) { return (0, (_hooks || _load_hooks()).callThroughHook)('auditStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { _this6.reporter.step(curr, total, _this6.reporter.lang('auditRunning'), emoji.get('mag')); if (_this6.flags.offline) { _this6.reporter.warn(_this6.reporter.lang('auditOffline')); return { bailout: false }; } const preparedManifests = yield _this6.prepareManifests(); // $FlowFixMe - Flow considers `m` in the map operation to be "mixed", so does not recognize `m.object` const mergedManifest = Object.assign({}, ...Object.values(preparedManifests).map(function (m) { return m.object; })); const auditVulnerabilityCounts = yield audit.performAudit(mergedManifest, _this6.lockfile, _this6.resolver, _this6.linker, topLevelPatterns); auditFoundProblems = auditVulnerabilityCounts.info || auditVulnerabilityCounts.low || auditVulnerabilityCounts.moderate || auditVulnerabilityCounts.high || auditVulnerabilityCounts.critical; return { bailout: yield _this6.bailout(topLevelPatterns, workspaceLayout) }; })); }); } steps.push(function (curr, total) { return (0, (_hooks || _load_hooks()).callThroughHook)('fetchStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { _this6.markIgnored(ignorePatterns); _this6.reporter.step(curr, total, _this6.reporter.lang('fetchingPackages'), emoji.get('truck')); const manifests = yield (_packageFetcher || _load_packageFetcher()).fetch(_this6.resolver.getManifests(), _this6.config); _this6.resolver.updateManifests(manifests); yield (_packageCompatibility || _load_packageCompatibility()).check(_this6.resolver.getManifests(), _this6.config, _this6.flags.ignoreEngines); })); }); steps.push(function (curr, total) { return (0, (_hooks || _load_hooks()).callThroughHook)('linkStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { // remove integrity hash to make this operation atomic yield _this6.integrityChecker.removeIntegrityFile(); _this6.reporter.step(curr, total, _this6.reporter.lang('linkingDependencies'), emoji.get('link')); flattenedTopLevelPatterns = _this6.preparePatternsForLinking(flattenedTopLevelPatterns, manifest, _this6.config.lockfileFolder === _this6.config.cwd); yield _this6.linker.init(flattenedTopLevelPatterns, workspaceLayout, { linkDuplicates: _this6.flags.linkDuplicates, ignoreOptional: _this6.flags.ignoreOptional }); })); }); if (_this6.config.plugnplayEnabled) { steps.push(function (curr, total) { return (0, (_hooks || _load_hooks()).callThroughHook)('pnpStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const pnpPath = `${_this6.config.lockfileFolder}/${(_constants || _load_constants()).PNP_FILENAME}`; const code = yield (0, (_generatePnpMap || _load_generatePnpMap()).generatePnpMap)(_this6.config, flattenedTopLevelPatterns, { resolver: _this6.resolver, reporter: _this6.reporter, targetPath: pnpPath, workspaceLayout }); try { const file = yield (_fs || _load_fs()).readFile(pnpPath); if (file === code) { return; } } catch (error) {} yield (_fs || _load_fs()).writeFile(pnpPath, code); yield (_fs || _load_fs()).chmod(pnpPath, 0o755); })); }); } steps.push(function (curr, total) { return (0, (_hooks || _load_hooks()).callThroughHook)('buildStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { _this6.reporter.step(curr, total, _this6.flags.force ? _this6.reporter.lang('rebuildingPackages') : _this6.reporter.lang('buildingFreshPackages'), emoji.get('hammer')); if (_this6.config.ignoreScripts) { _this6.reporter.warn(_this6.reporter.lang('ignoredScripts')); } else { yield _this6.scripts.init(flattenedTopLevelPatterns); } })); }); if (_this6.flags.har) { steps.push((() => { var _ref21 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (curr, total) { const formattedDate = new Date().toISOString().replace(/:/g, '-'); const filename = `yarn-install_${formattedDate}.har`; _this6.reporter.step(curr, total, _this6.reporter.lang('savingHar', filename), emoji.get('black_circle_for_record')); yield _this6.config.requestManager.saveHar(filename); }); return function (_x3, _x4) { return _ref21.apply(this, arguments); }; })()); } if (yield _this6.shouldClean()) { steps.push((() => { var _ref22 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (curr, total) { _this6.reporter.step(curr, total, _this6.reporter.lang('cleaningModules'), emoji.get('recycle')); yield (0, (_autoclean || _load_autoclean()).clean)(_this6.config, _this6.reporter); }); return function (_x5, _x6) { return _ref22.apply(this, arguments); }; })()); } let currentStep = 0; for (var _iterator11 = steps, _isArray11 = Array.isArray(_iterator11), _i11 = 0, _iterator11 = _isArray11 ? _iterator11 : _iterator11[Symbol.iterator]();;) { var _ref23; if (_isArray11) { if (_i11 >= _iterator11.length) break; _ref23 = _iterator11[_i11++]; } else { _i11 = _iterator11.next(); if (_i11.done) break; _ref23 = _i11.value; } const step = _ref23; const stepResult = yield step(++currentStep, steps.length); if (stepResult && stepResult.bailout) { if (_this6.flags.audit) { audit.summary(); } if (auditFoundProblems) { _this6.reporter.warn(_this6.reporter.lang('auditRunAuditForDetails')); } _this6.maybeOutputUpdate(); return flattenedTopLevelPatterns; } } // fin! if (_this6.flags.audit) { audit.summary(); } if (auditFoundProblems) { _this6.reporter.warn(_this6.reporter.lang('auditRunAuditForDetails')); } yield _this6.saveLockfileAndIntegrity(topLevelPatterns, workspaceLayout); yield _this6.persistChanges(); _this6.maybeOutputUpdate(); _this6.config.requestManager.clearCache(); return flattenedTopLevelPatterns; })(); } checkCompatibility() { var _this7 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { var _ref24 = yield _this7.fetchRequestFromCwd(); const manifest = _ref24.manifest; yield (_packageCompatibility || _load_packageCompatibility()).checkOne(manifest, _this7.config, _this7.flags.ignoreEngines); })(); } persistChanges() { var _this8 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { // get all the different registry manifests in this folder const manifests = yield _this8.config.getRootManifests(); if (yield _this8.applyChanges(manifests)) { yield _this8.config.saveRootManifests(manifests); } })(); } applyChanges(manifests) { let hasChanged = false; if (this.config.plugnplayPersist) { const object = manifests.npm.object; if (typeof object.installConfig !== 'object') { object.installConfig = {}; } if (this.config.plugnplayEnabled && object.installConfig.pnp !== true) { object.installConfig.pnp = true; hasChanged = true; } else if (!this.config.plugnplayEnabled && typeof object.installConfig.pnp !== 'undefined') { delete object.installConfig.pnp; hasChanged = true; } if (Object.keys(object.installConfig).length === 0) { delete object.installConfig; } } return Promise.resolve(hasChanged); } /** * Check if we should run the cleaning step. */ shouldClean() { return (_fs || _load_fs()).exists(path.join(this.config.lockfileFolder, (_constants || _load_constants()).CLEAN_FILENAME)); } /** * TODO */ flatten(patterns) { var _this9 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { if (!_this9.flags.flat) { return patterns; } const flattenedPatterns = []; for (var _iterator12 = _this9.resolver.getAllDependencyNamesByLevelOrder(patterns), _isArray12 = Array.isArray(_iterator12), _i12 = 0, _iterator12 = _isArray12 ? _iterator12 : _iterator12[Symbol.iterator]();;) { var _ref25; if (_isArray12) { if (_i12 >= _iterator12.length) break; _ref25 = _iterator12[_i12++]; } else { _i12 = _iterator12.next(); if (_i12.done) break; _ref25 = _i12.value; } const name = _ref25; const infos = _this9.resolver.getAllInfoForPackageName(name).filter(function (manifest) { const ref = manifest._reference; invariant(ref, 'expected package reference'); return !ref.ignore; }); if (infos.length === 0) { continue; } if (infos.length === 1) { // single version of this package // take out a single pattern as multiple patterns may have resolved to this package flattenedPatterns.push(_this9.resolver.patternsByPackage[name][0]); continue; } const options = infos.map(function (info) { const ref = info._reference; invariant(ref, 'expected reference'); return { // TODO `and is required by {PARENT}`, name: _this9.reporter.lang('manualVersionResolutionOption', ref.patterns.join(', '), info.version), value: info.version }; }); const versions = infos.map(function (info) { return info.version; }); let version; const resolutionVersion = _this9.resolutions[name]; if (resolutionVersion && versions.indexOf(resolutionVersion) >= 0) { // use json `resolution` version version = resolutionVersion; } else { version = yield _this9.reporter.select(_this9.reporter.lang('manualVersionResolution', name), _this9.reporter.lang('answer'), options); _this9.resolutions[name] = version; } flattenedPatterns.push(_this9.resolver.collapseAllVersionsOfPackage(name, version)); } // save resolutions to their appropriate root manifest if (Object.keys(_this9.resolutions).length) { const manifests = yield _this9.config.getRootManifests(); for (const name in _this9.resolutions) { const version = _this9.resolutions[name]; const patterns = _this9.resolver.patternsByPackage[name]; if (!patterns) { continue; } let manifest; for (var _iterator13 = patterns, _isArray13 = Array.isArray(_iterator13), _i13 = 0, _iterator13 = _isArray13 ? _iterator13 : _iterator13[Symbol.iterator]();;) { var _ref26; if (_isArray13) { if (_i13 >= _iterator13.length) break; _ref26 = _iterator13[_i13++]; } else { _i13 = _iterator13.next(); if (_i13.done) break; _ref26 = _i13.value; } const pattern = _ref26; manifest = _this9.resolver.getResolvedPattern(pattern); if (manifest) { break; } } invariant(manifest, 'expected manifest'); const ref = manifest._reference; invariant(ref, 'expected reference'); const object = manifests[ref.registry].object; object.resolutions = object.resolutions || {}; object.resolutions[name] = version; } yield _this9.config.saveRootManifests(manifests); } return flattenedPatterns; })(); } /** * Remove offline tarballs that are no longer required */ pruneOfflineMirror(lockfile) { var _this10 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const mirror = _this10.config.getOfflineMirrorPath(); if (!mirror) { return; } const requiredTarballs = new Set(); for (const dependency in lockfile) { const resolved = lockfile[dependency].resolved; if (resolved) { const basename = path.basename(resolved.split('#')[0]); if (dependency[0] === '@' && basename[0] !== '@') { requiredTarballs.add(`${dependency.split('/')[0]}-${basename}`); } requiredTarballs.add(basename); } } const mirrorFiles = yield (_fs || _load_fs()).walk(mirror); for (var _iterator14 = mirrorFiles, _isArray14 = Array.isArray(_iterator14), _i14 = 0, _iterator14 = _isArray14 ? _iterator14 : _iterator14[Symbol.iterator]();;) { var _ref27; if (_isArray14) { if (_i14 >= _iterator14.length) break; _ref27 = _iterator14[_i14++]; } else { _i14 = _iterator14.next(); if (_i14.done) break; _ref27 = _i14.value; } const file = _ref27; const isTarball = path.extname(file.basename) === '.tgz'; // if using experimental-pack-script-packages-in-mirror flag, don't unlink prebuilt packages const hasPrebuiltPackage = file.relative.startsWith('prebuilt/'); if (isTarball && !hasPrebuiltPackage && !requiredTarballs.has(file.basename)) { yield (_fs || _load_fs()).unlink(file.absolute); } } })(); } /** * Save updated integrity and lockfiles. */ saveLockfileAndIntegrity(patterns, workspaceLayout) { var _this11 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const resolvedPatterns = {}; Object.keys(_this11.resolver.patterns).forEach(function (pattern) { if (!workspaceLayout || !workspaceLayout.getManifestByPattern(pattern)) { resolvedPatterns[pattern] = _this11.resolver.patterns[pattern]; } }); // TODO this code is duplicated in a few places, need a common way to filter out workspace patterns from lockfile patterns = patterns.filter(function (p) { return !workspaceLayout || !workspaceLayout.getManifestByPattern(p); }); const lockfileBasedOnResolver = _this11.lockfile.getLockfile(resolvedPatterns); if (_this11.config.pruneOfflineMirror) { yield _this11.pruneOfflineMirror(lockfileBasedOnResolver); } // write integrity hash if (!_this11.config.plugnplayEnabled) { yield _this11.integrityChecker.save(patterns, lockfileBasedOnResolver, _this11.flags, workspaceLayout, _this11.scripts.getArtifacts()); } // --no-lockfile or --pure-lockfile or --frozen-lockfile if (_this11.flags.lockfile === false || _this11.flags.pureLockfile || _this11.flags.frozenLockfile) { return; } const lockFileHasAllPatterns = patterns.every(function (p) { return _this11.lockfile.getLocked(p); }); const lockfilePatternsMatch = Object.keys(_this11.lockfile.cache || {}).every(function (p) { return lockfileBasedOnResolver[p]; }); const resolverPatternsAreSameAsInLockfile = Object.keys(lockfileBasedOnResolver).every(function (pattern) { const manifest = _this11.lockfile.getLocked(pattern); return manifest && manifest.resolved === lockfileBasedOnResolver[pattern].resolved && deepEqual(manifest.prebuiltVariants, lockfileBasedOnResolver[pattern].prebuiltVariants); }); const integrityPatternsAreSameAsInLockfile = Object.keys(lockfileBasedOnResolver).every(function (pattern) { const existingIntegrityInfo = lockfileBasedOnResolver[pattern].integrity; if (!existingIntegrityInfo) { // if this entry does not have an integrity, no need to re-write the lockfile because of it return true; } const manifest = _this11.lockfile.getLocked(pattern); if (manifest && manifest.integrity) { const manifestIntegrity = ssri.stringify(manifest.integrity); return manifestIntegrity === existingIntegrityInfo; } return false; }); // remove command is followed by install with force, lockfile will be rewritten in any case then if (!_this11.flags.force && _this11.lockfile.parseResultType === 'success' && lockFileHasAllPatterns && lockfilePatternsMatch && resolverPatternsAreSameAsInLockfile && integrityPatternsAreSameAsInLockfile && patterns.length) { return; } // build lockfile location const loc = path.join(_this11.config.lockfileFolder, (_constants || _load_constants()).LOCKFILE_FILENAME); // write lockfile const lockSource = (0, (_lockfile2 || _load_lockfile2()).stringify)(lockfileBasedOnResolver, false, _this11.config.enableLockfileVersions); yield (_fs || _load_fs()).writeFilePreservingEol(loc, lockSource); _this11._logSuccessSaveLockfile(); })(); } _logSuccessSaveLockfile() { this.reporter.success(this.reporter.lang('savedLockfile')); } /** * Load the dependency graph of the current install. Only does package resolving and wont write to the cwd. */ hydrate(ignoreUnusedPatterns) { var _this12 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const request = yield _this12.fetchRequestFromCwd([], ignoreUnusedPatterns); const depRequests = request.requests, rawPatterns = request.patterns, ignorePatterns = request.ignorePatterns, workspaceLayout = request.workspaceLayout; yield _this12.resolver.init(depRequests, { isFlat: _this12.flags.flat, isFrozen: _this12.flags.frozenLockfile, workspaceLayout }); yield _this12.flatten(rawPatterns); _this12.markIgnored(ignorePatterns); // fetch packages, should hit cache most of the time const manifests = yield (_packageFetcher || _load_packageFetcher()).fetch(_this12.resolver.getManifests(), _this12.config); _this12.resolver.updateManifests(manifests); yield (_packageCompatibility || _load_packageCompatibility()).check(_this12.resolver.getManifests(), _this12.config, _this12.flags.ignoreEngines); // expand minimal manifests for (var _iterator15 = _this12.resolver.getManifests(), _isArray15 = Array.isArray(_iterator15), _i15 = 0, _iterator15 = _isArray15 ? _iterator15 : _iterator15[Symbol.iterator]();;) { var _ref28; if (_isArray15) { if (_i15 >= _iterator15.length) break; _ref28 = _iterator15[_i15++]; } else { _i15 = _iterator15.next(); if (_i15.done) break; _ref28 = _i15.value; } const manifest = _ref28; const ref = manifest._reference; invariant(ref, 'expected reference'); const type = ref.remote.type; // link specifier won't ever hit cache let loc = ''; if (type === 'link') { continue; } else if (type === 'workspace') { if (!ref.remote.reference) { continue; } loc = ref.remote.reference; } else { loc = _this12.config.generateModuleCachePath(ref); } const newPkg = yield _this12.config.readManifest(loc); yield _this12.resolver.updateManifest(ref, newPkg); } return request; })(); } /** * Check for updates every day and output a nag message if there's a newer version. */ checkUpdate() { if (this.config.nonInteractive) { // don't show upgrade dialog on CI or non-TTY terminals return; } // don't check if disabled if (this.config.getOption('disable-self-update-check')) { return; } // only check for updates once a day const lastUpdateCheck = Number(this.config.getOption('lastUpdateCheck')) || 0; if (lastUpdateCheck && Date.now() - lastUpdateCheck < ONE_DAY) { return; } // don't bug for updates on tagged releases if ((_yarnVersion || _load_yarnVersion()).version.indexOf('-') >= 0) { return; } this._checkUpdate().catch(() => { // swallow errors }); } _checkUpdate() { var _this13 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { let latestVersion = yield _this13.config.requestManager.request({ url: (_constants || _load_constants()).SELF_UPDATE_VERSION_URL }); invariant(typeof latestVersion === 'string', 'expected string'); latestVersion = latestVersion.trim(); if (!semver.valid(latestVersion)) { return; } // ensure we only check for updates periodically _this13.config.registries.yarn.saveHomeConfig({ lastUpdateCheck: Date.now() }); if (semver.gt(latestVersion, (_yarnVersion || _load_yarnVersion()).version)) { const installationMethod = yield (0, (_yarnVersion || _load_yarnVersion()).getInstallationMethod)(); _this13.maybeOutputUpdate = function () { _this13.reporter.warn(_this13.reporter.lang('yarnOutdated', latestVersion, (_yarnVersion || _load_yarnVersion()).version)); const command = getUpdateCommand(installationMethod); if (command) { _this13.reporter.info(_this13.reporter.lang('yarnOutdatedCommand')); _this13.reporter.command(command); } else { const installer = getUpdateInstaller(installationMethod); if (installer) { _this13.reporter.info(_this13.reporter.lang('yarnOutdatedInstaller', installer)); } } }; } })(); } /** * Method to override with a possible upgrade message. */ maybeOutputUpdate() {} } exports.Install = Install; function hasWrapper(commander, args) { return true; } function setFlags(commander) { commander.description('Yarn install is used to install all dependencies for a project.'); commander.usage('install [flags]'); commander.option('-A, --audit', 'Run vulnerability audit on installed packages'); commander.option('-g, --global', 'DEPRECATED'); commander.option('-S, --save', 'DEPRECATED - save package to your `dependencies`'); commander.option('-D, --save-dev', 'DEPRECATED - save package to your `devDependencies`'); commander.option('-P, --save-peer', 'DEPRECATED - save package to your `peerDependencies`'); commander.option('-O, --save-optional', 'DEPRECATED - save package to your `optionalDependencies`'); commander.option('-E, --save-exact', 'DEPRECATED'); commander.option('-T, --save-tilde', 'DEPRECATED'); } /***/ }), /* 35 */ /***/ (function(module, exports, __webpack_require__) { var isObject = __webpack_require__(53); module.exports = function (it) { if (!isObject(it)) throw TypeError(it + ' is not an object!'); return it; }; /***/ }), /* 36 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return SubjectSubscriber; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Subject; }); /* unused harmony export AnonymousSubject */ /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_tslib__ = __webpack_require__(1); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Observable__ = __webpack_require__(12); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__Subscriber__ = __webpack_require__(7); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__Subscription__ = __webpack_require__(25); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__ = __webpack_require__(190); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__SubjectSubscription__ = __webpack_require__(422); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__internal_symbol_rxSubscriber__ = __webpack_require__(321); /** PURE_IMPORTS_START tslib,_Observable,_Subscriber,_Subscription,_util_ObjectUnsubscribedError,_SubjectSubscription,_internal_symbol_rxSubscriber PURE_IMPORTS_END */ var SubjectSubscriber = /*@__PURE__*/ (function (_super) { __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](SubjectSubscriber, _super); function SubjectSubscriber(destination) { var _this = _super.call(this, destination) || this; _this.destination = destination; return _this; } return SubjectSubscriber; }(__WEBPACK_IMPORTED_MODULE_2__Subscriber__["a" /* Subscriber */])); var Subject = /*@__PURE__*/ (function (_super) { __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](Subject, _super); function Subject() { var _this = _super.call(this) || this; _this.observers = []; _this.closed = false; _this.isStopped = false; _this.hasError = false; _this.thrownError = null; return _this; } Subject.prototype[__WEBPACK_IMPORTED_MODULE_6__internal_symbol_rxSubscriber__["a" /* rxSubscriber */]] = function () { return new SubjectSubscriber(this); }; Subject.prototype.lift = function (operator) { var subject = new AnonymousSubject(this, this); subject.operator = operator; return subject; }; Subject.prototype.next = function (value) { if (this.closed) { throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); } if (!this.isStopped) { var observers = this.observers; var len = observers.length; var copy = observers.slice(); for (var i = 0; i < len; i++) { copy[i].next(value); } } }; Subject.prototype.error = function (err) { if (this.closed) { throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); } this.hasError = true; this.thrownError = err; this.isStopped = true; var observers = this.observers; var len = observers.length; var copy = observers.slice(); for (var i = 0; i < len; i++) { copy[i].error(err); } this.observers.length = 0; }; Subject.prototype.complete = function () { if (this.closed) { throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); } this.isStopped = true; var observers = this.observers; var len = observers.length; var copy = observers.slice(); for (var i = 0; i < len; i++) { copy[i].complete(); } this.observers.length = 0; }; Subject.prototype.unsubscribe = function () { this.isStopped = true; this.closed = true; this.observers = null; }; Subject.prototype._trySubscribe = function (subscriber) { if (this.closed) { throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); } else { return _super.prototype._trySubscribe.call(this, subscriber); } }; Subject.prototype._subscribe = function (subscriber) { if (this.closed) { throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); } else if (this.hasError) { subscriber.error(this.thrownError); return __WEBPACK_IMPORTED_MODULE_3__Subscription__["a" /* Subscription */].EMPTY; } else if (this.isStopped) { subscriber.complete(); return __WEBPACK_IMPORTED_MODULE_3__Subscription__["a" /* Subscription */].EMPTY; } else { this.observers.push(subscriber); return new __WEBPACK_IMPORTED_MODULE_5__SubjectSubscription__["a" /* SubjectSubscription */](this, subscriber); } }; Subject.prototype.asObservable = function () { var observable = new __WEBPACK_IMPORTED_MODULE_1__Observable__["a" /* Observable */](); observable.source = this; return observable; }; Subject.create = function (destination, source) { return new AnonymousSubject(destination, source); }; return Subject; }(__WEBPACK_IMPORTED_MODULE_1__Observable__["a" /* Observable */])); var AnonymousSubject = /*@__PURE__*/ (function (_super) { __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](AnonymousSubject, _super); function AnonymousSubject(destination, source) { var _this = _super.call(this) || this; _this.destination = destination; _this.source = source; return _this; } AnonymousSubject.prototype.next = function (value) { var destination = this.destination; if (destination && destination.next) { destination.next(value); } }; AnonymousSubject.prototype.error = function (err) { var destination = this.destination; if (destination && destination.error) { this.destination.error(err); } }; AnonymousSubject.prototype.complete = function () { var destination = this.destination; if (destination && destination.complete) { this.destination.complete(); } }; AnonymousSubject.prototype._subscribe = function (subscriber) { var source = this.source; if (source) { return this.source.subscribe(subscriber); } else { return __WEBPACK_IMPORTED_MODULE_3__Subscription__["a" /* Subscription */].EMPTY; } }; return AnonymousSubject; }(Subject)); //# sourceMappingURL=Subject.js.map /***/ }), /* 37 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.normalizePattern = normalizePattern; /** * Explode and normalize a pattern into its name and range. */ function normalizePattern(pattern) { let hasVersion = false; let range = 'latest'; let name = pattern; // if we're a scope then remove the @ and add it back later let isScoped = false; if (name[0] === '@') { isScoped = true; name = name.slice(1); } // take first part as the name const parts = name.split('@'); if (parts.length > 1) { name = parts.shift(); range = parts.join('@'); if (range) { hasVersion = true; } else { range = '*'; } } // add back @ scope suffix if (isScoped) { name = `@${name}`; } return { name, range, hasVersion }; } /***/ }), /* 38 */ /***/ (function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(module) {var __WEBPACK_AMD_DEFINE_RESULT__;/** * @license * Lodash * Copyright JS Foundation and other contributors * Released under MIT license * Based on Underscore.js 1.8.3 * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors */ ;(function() { /** Used as a safe reference for `undefined` in pre-ES5 environments. */ var undefined; /** Used as the semantic version number. */ var VERSION = '4.17.10'; /** Used as the size to enable large array optimizations. */ var LARGE_ARRAY_SIZE = 200; /** Error message constants. */ var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.', FUNC_ERROR_TEXT = 'Expected a function'; /** Used to stand-in for `undefined` hash values. */ var HASH_UNDEFINED = '__lodash_hash_undefined__'; /** Used as the maximum memoize cache size. */ var MAX_MEMOIZE_SIZE = 500; /** Used as the internal argument placeholder. */ var PLACEHOLDER = '__lodash_placeholder__'; /** Used to compose bitmasks for cloning. */ var CLONE_DEEP_FLAG = 1, CLONE_FLAT_FLAG = 2, CLONE_SYMBOLS_FLAG = 4; /** Used to compose bitmasks for value comparisons. */ var COMPARE_PARTIAL_FLAG = 1, COMPARE_UNORDERED_FLAG = 2; /** Used to compose bitmasks for function metadata. */ var WRAP_BIND_FLAG = 1, WRAP_BIND_KEY_FLAG = 2, WRAP_CURRY_BOUND_FLAG = 4, WRAP_CURRY_FLAG = 8, WRAP_CURRY_RIGHT_FLAG = 16, WRAP_PARTIAL_FLAG = 32, WRAP_PARTIAL_RIGHT_FLAG = 64, WRAP_ARY_FLAG = 128, WRAP_REARG_FLAG = 256, WRAP_FLIP_FLAG = 512; /** Used as default options for `_.truncate`. */ var DEFAULT_TRUNC_LENGTH = 30, DEFAULT_TRUNC_OMISSION = '...'; /** Used to detect hot functions by number of calls within a span of milliseconds. */ var HOT_COUNT = 800, HOT_SPAN = 16; /** Used to indicate the type of lazy iteratees. */ var LAZY_FILTER_FLAG = 1, LAZY_MAP_FLAG = 2, LAZY_WHILE_FLAG = 3; /** Used as references for various `Number` constants. */ var INFINITY = 1 / 0, MAX_SAFE_INTEGER = 9007199254740991, MAX_INTEGER = 1.7976931348623157e+308, NAN = 0 / 0; /** Used as references for the maximum length and index of an array. */ var MAX_ARRAY_LENGTH = 4294967295, MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; /** Used to associate wrap methods with their bit flags. */ var wrapFlags = [ ['ary', WRAP_ARY_FLAG], ['bind', WRAP_BIND_FLAG], ['bindKey', WRAP_BIND_KEY_FLAG], ['curry', WRAP_CURRY_FLAG], ['curryRight', WRAP_CURRY_RIGHT_FLAG], ['flip', WRAP_FLIP_FLAG], ['partial', WRAP_PARTIAL_FLAG], ['partialRight', WRAP_PARTIAL_RIGHT_FLAG], ['rearg', WRAP_REARG_FLAG] ]; /** `Object#toString` result references. */ var argsTag = '[object Arguments]', arrayTag = '[object Array]', asyncTag = '[object AsyncFunction]', boolTag = '[object Boolean]', dateTag = '[object Date]', domExcTag = '[object DOMException]', errorTag = '[object Error]', funcTag = '[object Function]', genTag = '[object GeneratorFunction]', mapTag = '[object Map]', numberTag = '[object Number]', nullTag = '[object Null]', objectTag = '[object Object]', promiseTag = '[object Promise]', proxyTag = '[object Proxy]', regexpTag = '[object RegExp]', setTag = '[object Set]', stringTag = '[object String]', symbolTag = '[object Symbol]', undefinedTag = '[object Undefined]', weakMapTag = '[object WeakMap]', weakSetTag = '[object WeakSet]'; var arrayBufferTag = '[object ArrayBuffer]', dataViewTag = '[object DataView]', float32Tag = '[object Float32Array]', float64Tag = '[object Float64Array]', int8Tag = '[object Int8Array]', int16Tag = '[object Int16Array]', int32Tag = '[object Int32Array]', uint8Tag = '[object Uint8Array]', uint8ClampedTag = '[object Uint8ClampedArray]', uint16Tag = '[object Uint16Array]', uint32Tag = '[object Uint32Array]'; /** Used to match empty string literals in compiled template source. */ var reEmptyStringLeading = /\b__p \+= '';/g, reEmptyStringMiddle = /\b(__p \+=) '' \+/g, reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; /** Used to match HTML entities and HTML characters. */ var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g, reUnescapedHtml = /[&<>"']/g, reHasEscapedHtml = RegExp(reEscapedHtml.source), reHasUnescapedHtml = RegExp(reUnescapedHtml.source); /** Used to match template delimiters. */ var reEscape = /<%-([\s\S]+?)%>/g, reEvaluate = /<%([\s\S]+?)%>/g, reInterpolate = /<%=([\s\S]+?)%>/g; /** Used to match property names within property paths. */ var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, reIsPlainProp = /^\w*$/, rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; /** * Used to match `RegExp` * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). */ var reRegExpChar = /[\\^$.*+?()[\]{}|]/g, reHasRegExpChar = RegExp(reRegExpChar.source); /** Used to match leading and trailing whitespace. */ var reTrim = /^\s+|\s+$/g, reTrimStart = /^\s+/, reTrimEnd = /\s+$/; /** Used to match wrap detail comments. */ var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/, reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/, reSplitDetails = /,? & /; /** Used to match words composed of alphanumeric characters. */ var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; /** Used to match backslashes in property paths. */ var reEscapeChar = /\\(\\)?/g; /** * Used to match * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components). */ var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; /** Used to match `RegExp` flags from their coerced string values. */ var reFlags = /\w*$/; /** Used to detect bad signed hexadecimal string values. */ var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; /** Used to detect binary string values. */ var reIsBinary = /^0b[01]+$/i; /** Used to detect host constructors (Safari). */ var reIsHostCtor = /^\[object .+?Constructor\]$/; /** Used to detect octal string values. */ var reIsOctal = /^0o[0-7]+$/i; /** Used to detect unsigned integer values. */ var reIsUint = /^(?:0|[1-9]\d*)$/; /** Used to match Latin Unicode letters (excluding mathematical operators). */ var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; /** Used to ensure capturing order of template delimiters. */ var reNoMatch = /($^)/; /** Used to match unescaped characters in compiled string literals. */ var reUnescapedString = /['\n\r\u2028\u2029\\]/g; /** Used to compose unicode character classes. */ var rsAstralRange = '\\ud800-\\udfff', rsComboMarksRange = '\\u0300-\\u036f', reComboHalfMarksRange = '\\ufe20-\\ufe2f', rsComboSymbolsRange = '\\u20d0-\\u20ff', rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange, rsDingbatRange = '\\u2700-\\u27bf', rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff', rsMathOpRange = '\\xac\\xb1\\xd7\\xf7', rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf', rsPunctuationRange = '\\u2000-\\u206f', rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000', rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde', rsVarRange = '\\ufe0e\\ufe0f', rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange; /** Used to compose unicode capture groups. */ var rsApos = "['\u2019]", rsAstral = '[' + rsAstralRange + ']', rsBreak = '[' + rsBreakRange + ']', rsCombo = '[' + rsComboRange + ']', rsDigits = '\\d+', rsDingbat = '[' + rsDingbatRange + ']', rsLower = '[' + rsLowerRange + ']', rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']', rsFitz = '\\ud83c[\\udffb-\\udfff]', rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')', rsNonAstral = '[^' + rsAstralRange + ']', rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}', rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]', rsUpper = '[' + rsUpperRange + ']', rsZWJ = '\\u200d'; /** Used to compose unicode regexes. */ var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')', rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')', rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?', rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?', reOptMod = rsModifier + '?', rsOptVar = '[' + rsVarRange + ']?', rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*', rsOrdLower = '\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])', rsOrdUpper = '\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])', rsSeq = rsOptVar + reOptMod + rsOptJoin, rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq, rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; /** Used to match apostrophes. */ var reApos = RegExp(rsApos, 'g'); /** * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). */ var reComboMark = RegExp(rsCombo, 'g'); /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); /** Used to match complex or compound words. */ var reUnicodeWord = RegExp([ rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')', rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower, rsUpper + '+' + rsOptContrUpper, rsOrdUpper, rsOrdLower, rsDigits, rsEmoji ].join('|'), 'g'); /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */ var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']'); /** Used to detect strings that need a more robust regexp to match words. */ var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/; /** Used to assign default `context` object properties. */ var contextProps = [ 'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array', 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object', 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout' ]; /** Used to make template sourceURLs easier to identify. */ var templateCounter = -1; /** Used to identify `toStringTag` values of typed arrays. */ var typedArrayTags = {}; typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true; typedArrayTags[argsTag] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; /** Used to identify `toStringTag` values supported by `_.clone`. */ var cloneableTags = {}; cloneableTags[argsTag] = cloneableTags[arrayTag] = cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = cloneableTags[boolTag] = cloneableTags[dateTag] = cloneableTags[float32Tag] = cloneableTags[float64Tag] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[mapTag] = cloneableTags[numberTag] = cloneableTags[objectTag] = cloneableTags[regexpTag] = cloneableTags[setTag] = cloneableTags[stringTag] = cloneableTags[symbolTag] = cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; cloneableTags[errorTag] = cloneableTags[funcTag] = cloneableTags[weakMapTag] = false; /** Used to map Latin Unicode letters to basic Latin letters. */ var deburredLetters = { // Latin-1 Supplement block. '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', '\xc7': 'C', '\xe7': 'c', '\xd0': 'D', '\xf0': 'd', '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', '\xd1': 'N', '\xf1': 'n', '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', '\xc6': 'Ae', '\xe6': 'ae', '\xde': 'Th', '\xfe': 'th', '\xdf': 'ss', // Latin Extended-A block. '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', '\u0134': 'J', '\u0135': 'j', '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', '\u0163': 't', '\u0165': 't', '\u0167': 't', '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', '\u0174': 'W', '\u0175': 'w', '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', '\u0132': 'IJ', '\u0133': 'ij', '\u0152': 'Oe', '\u0153': 'oe', '\u0149': "'n", '\u017f': 's' }; /** Used to map characters to HTML entities. */ var htmlEscapes = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; /** Used to map HTML entities to characters. */ var htmlUnescapes = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'" }; /** Used to escape characters for inclusion in compiled string literals. */ var stringEscapes = { '\\': '\\', "'": "'", '\n': 'n', '\r': 'r', '\u2028': 'u2028', '\u2029': 'u2029' }; /** Built-in method references without a dependency on `root`. */ var freeParseFloat = parseFloat, freeParseInt = parseInt; /** Detect free variable `global` from Node.js. */ var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; /** Detect free variable `self`. */ var freeSelf = typeof self == 'object' && self && self.Object === Object && self; /** Used as a reference to the global object. */ var root = freeGlobal || freeSelf || Function('return this')(); /** Detect free variable `exports`. */ var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; /** Detect free variable `module`. */ var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; /** Detect the popular CommonJS extension `module.exports`. */ var moduleExports = freeModule && freeModule.exports === freeExports; /** Detect free variable `process` from Node.js. */ var freeProcess = moduleExports && freeGlobal.process; /** Used to access faster Node.js helpers. */ var nodeUtil = (function() { try { // Use `util.types` for Node.js 10+. var types = freeModule && freeModule.require && freeModule.require('util').types; if (types) { return types; } // Legacy `process.binding('util')` for Node.js < 10. return freeProcess && freeProcess.binding && freeProcess.binding('util'); } catch (e) {} }()); /* Node.js helper references. */ var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer, nodeIsDate = nodeUtil && nodeUtil.isDate, nodeIsMap = nodeUtil && nodeUtil.isMap, nodeIsRegExp = nodeUtil && nodeUtil.isRegExp, nodeIsSet = nodeUtil && nodeUtil.isSet, nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; /*--------------------------------------------------------------------------*/ /** * A faster alternative to `Function#apply`, this function invokes `func` * with the `this` binding of `thisArg` and the arguments of `args`. * * @private * @param {Function} func The function to invoke. * @param {*} thisArg The `this` binding of `func`. * @param {Array} args The arguments to invoke `func` with. * @returns {*} Returns the result of `func`. */ function apply(func, thisArg, args) { switch (args.length) { case 0: return func.call(thisArg); case 1: return func.call(thisArg, args[0]); case 2: return func.call(thisArg, args[0], args[1]); case 3: return func.call(thisArg, args[0], args[1], args[2]); } return func.apply(thisArg, args); } /** * A specialized version of `baseAggregator` for arrays. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} setter The function to set `accumulator` values. * @param {Function} iteratee The iteratee to transform keys. * @param {Object} accumulator The initial aggregated object. * @returns {Function} Returns `accumulator`. */ function arrayAggregator(array, setter, iteratee, accumulator) { var index = -1, length = array == null ? 0 : array.length; while (++index < length) { var value = array[index]; setter(accumulator, value, iteratee(value), array); } return accumulator; } /** * A specialized version of `_.forEach` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns `array`. */ function arrayEach(array, iteratee) { var index = -1, length = array == null ? 0 : array.length; while (++index < length) { if (iteratee(array[index], index, array) === false) { break; } } return array; } /** * A specialized version of `_.forEachRight` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns `array`. */ function arrayEachRight(array, iteratee) { var length = array == null ? 0 : array.length; while (length--) { if (iteratee(array[length], length, array) === false) { break; } } return array; } /** * A specialized version of `_.every` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if all elements pass the predicate check, * else `false`. */ function arrayEvery(array, predicate) { var index = -1, length = array == null ? 0 : array.length; while (++index < length) { if (!predicate(array[index], index, array)) { return false; } } return true; } /** * A specialized version of `_.filter` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {Array} Returns the new filtered array. */ function arrayFilter(array, predicate) { var index = -1, length = array == null ? 0 : array.length, resIndex = 0, result = []; while (++index < length) { var value = array[index]; if (predicate(value, index, array)) { result[resIndex++] = value; } } return result; } /** * A specialized version of `_.includes` for arrays without support for * specifying an index to search from. * * @private * @param {Array} [array] The array to inspect. * @param {*} target The value to search for. * @returns {boolean} Returns `true` if `target` is found, else `false`. */ function arrayIncludes(array, value) { var length = array == null ? 0 : array.length; return !!length && baseIndexOf(array, value, 0) > -1; } /** * This function is like `arrayIncludes` except that it accepts a comparator. * * @private * @param {Array} [array] The array to inspect. * @param {*} target The value to search for. * @param {Function} comparator The comparator invoked per element. * @returns {boolean} Returns `true` if `target` is found, else `false`. */ function arrayIncludesWith(array, value, comparator) { var index = -1, length = array == null ? 0 : array.length; while (++index < length) { if (comparator(value, array[index])) { return true; } } return false; } /** * A specialized version of `_.map` for arrays without support for iteratee * shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the new mapped array. */ function arrayMap(array, iteratee) { var index = -1, length = array == null ? 0 : array.length, result = Array(length); while (++index < length) { result[index] = iteratee(array[index], index, array); } return result; } /** * Appends the elements of `values` to `array`. * * @private * @param {Array} array The array to modify. * @param {Array} values The values to append. * @returns {Array} Returns `array`. */ function arrayPush(array, values) { var index = -1, length = values.length, offset = array.length; while (++index < length) { array[offset + index] = values[index]; } return array; } /** * A specialized version of `_.reduce` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {*} [accumulator] The initial value. * @param {boolean} [initAccum] Specify using the first element of `array` as * the initial value. * @returns {*} Returns the accumulated value. */ function arrayReduce(array, iteratee, accumulator, initAccum) { var index = -1, length = array == null ? 0 : array.length; if (initAccum && length) { accumulator = array[++index]; } while (++index < length) { accumulator = iteratee(accumulator, array[index], index, array); } return accumulator; } /** * A specialized version of `_.reduceRight` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {*} [accumulator] The initial value. * @param {boolean} [initAccum] Specify using the last element of `array` as * the initial value. * @returns {*} Returns the accumulated value. */ function arrayReduceRight(array, iteratee, accumulator, initAccum) { var length = array == null ? 0 : array.length; if (initAccum && length) { accumulator = array[--length]; } while (length--) { accumulator = iteratee(accumulator, array[length], length, array); } return accumulator; } /** * A specialized version of `_.some` for arrays without support for iteratee * shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if any element passes the predicate check, * else `false`. */ function arraySome(array, predicate) { var index = -1, length = array == null ? 0 : array.length; while (++index < length) { if (predicate(array[index], index, array)) { return true; } } return false; } /** * Gets the size of an ASCII `string`. * * @private * @param {string} string The string inspect. * @returns {number} Returns the string size. */ var asciiSize = baseProperty('length'); /** * Converts an ASCII `string` to an array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the converted array. */ function asciiToArray(string) { return string.split(''); } /** * Splits an ASCII `string` into an array of its words. * * @private * @param {string} The string to inspect. * @returns {Array} Returns the words of `string`. */ function asciiWords(string) { return string.match(reAsciiWord) || []; } /** * The base implementation of methods like `_.findKey` and `_.findLastKey`, * without support for iteratee shorthands, which iterates over `collection` * using `eachFunc`. * * @private * @param {Array|Object} collection The collection to inspect. * @param {Function} predicate The function invoked per iteration. * @param {Function} eachFunc The function to iterate over `collection`. * @returns {*} Returns the found element or its key, else `undefined`. */ function baseFindKey(collection, predicate, eachFunc) { var result; eachFunc(collection, function(value, key, collection) { if (predicate(value, key, collection)) { result = key; return false; } }); return result; } /** * The base implementation of `_.findIndex` and `_.findLastIndex` without * support for iteratee shorthands. * * @private * @param {Array} array The array to inspect. * @param {Function} predicate The function invoked per iteration. * @param {number} fromIndex The index to search from. * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {number} Returns the index of the matched value, else `-1`. */ function baseFindIndex(array, predicate, fromIndex, fromRight) { var length = array.length, index = fromIndex + (fromRight ? 1 : -1); while ((fromRight ? index-- : ++index < length)) { if (predicate(array[index], index, array)) { return index; } } return -1; } /** * The base implementation of `_.indexOf` without `fromIndex` bounds checks. * * @private * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} fromIndex The index to search from. * @returns {number} Returns the index of the matched value, else `-1`. */ function baseIndexOf(array, value, fromIndex) { return value === value ? strictIndexOf(array, value, fromIndex) : baseFindIndex(array, baseIsNaN, fromIndex); } /** * This function is like `baseIndexOf` except that it accepts a comparator. * * @private * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} fromIndex The index to search from. * @param {Function} comparator The comparator invoked per element. * @returns {number} Returns the index of the matched value, else `-1`. */ function baseIndexOfWith(array, value, fromIndex, comparator) { var index = fromIndex - 1, length = array.length; while (++index < length) { if (comparator(array[index], value)) { return index; } } return -1; } /** * The base implementation of `_.isNaN` without support for number objects. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. */ function baseIsNaN(value) { return value !== value; } /** * The base implementation of `_.mean` and `_.meanBy` without support for * iteratee shorthands. * * @private * @param {Array} array The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {number} Returns the mean. */ function baseMean(array, iteratee) { var length = array == null ? 0 : array.length; return length ? (baseSum(array, iteratee) / length) : NAN; } /** * The base implementation of `_.property` without support for deep paths. * * @private * @param {string} key The key of the property to get. * @returns {Function} Returns the new accessor function. */ function baseProperty(key) { return function(object) { return object == null ? undefined : object[key]; }; } /** * The base implementation of `_.propertyOf` without support for deep paths. * * @private * @param {Object} object The object to query. * @returns {Function} Returns the new accessor function. */ function basePropertyOf(object) { return function(key) { return object == null ? undefined : object[key]; }; } /** * The base implementation of `_.reduce` and `_.reduceRight`, without support * for iteratee shorthands, which iterates over `collection` using `eachFunc`. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {*} accumulator The initial value. * @param {boolean} initAccum Specify using the first or last element of * `collection` as the initial value. * @param {Function} eachFunc The function to iterate over `collection`. * @returns {*} Returns the accumulated value. */ function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) { eachFunc(collection, function(value, index, collection) { accumulator = initAccum ? (initAccum = false, value) : iteratee(accumulator, value, index, collection); }); return accumulator; } /** * The base implementation of `_.sortBy` which uses `comparer` to define the * sort order of `array` and replaces criteria objects with their corresponding * values. * * @private * @param {Array} array The array to sort. * @param {Function} comparer The function to define sort order. * @returns {Array} Returns `array`. */ function baseSortBy(array, comparer) { var length = array.length; array.sort(comparer); while (length--) { array[length] = array[length].value; } return array; } /** * The base implementation of `_.sum` and `_.sumBy` without support for * iteratee shorthands. * * @private * @param {Array} array The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {number} Returns the sum. */ function baseSum(array, iteratee) { var result, index = -1, length = array.length; while (++index < length) { var current = iteratee(array[index]); if (current !== undefined) { result = result === undefined ? current : (result + current); } } return result; } /** * The base implementation of `_.times` without support for iteratee shorthands * or max array length checks. * * @private * @param {number} n The number of times to invoke `iteratee`. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the array of results. */ function baseTimes(n, iteratee) { var index = -1, result = Array(n); while (++index < n) { result[index] = iteratee(index); } return result; } /** * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array * of key-value pairs for `object` corresponding to the property names of `props`. * * @private * @param {Object} object The object to query. * @param {Array} props The property names to get values for. * @returns {Object} Returns the key-value pairs. */ function baseToPairs(object, props) { return arrayMap(props, function(key) { return [key, object[key]]; }); } /** * The base implementation of `_.unary` without support for storing metadata. * * @private * @param {Function} func The function to cap arguments for. * @returns {Function} Returns the new capped function. */ function baseUnary(func) { return function(value) { return func(value); }; } /** * The base implementation of `_.values` and `_.valuesIn` which creates an * array of `object` property values corresponding to the property names * of `props`. * * @private * @param {Object} object The object to query. * @param {Array} props The property names to get values for. * @returns {Object} Returns the array of property values. */ function baseValues(object, props) { return arrayMap(props, function(key) { return object[key]; }); } /** * Checks if a `cache` value for `key` exists. * * @private * @param {Object} cache The cache to query. * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function cacheHas(cache, key) { return cache.has(key); } /** * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol * that is not found in the character symbols. * * @private * @param {Array} strSymbols The string symbols to inspect. * @param {Array} chrSymbols The character symbols to find. * @returns {number} Returns the index of the first unmatched string symbol. */ function charsStartIndex(strSymbols, chrSymbols) { var index = -1, length = strSymbols.length; while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} return index; } /** * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol * that is not found in the character symbols. * * @private * @param {Array} strSymbols The string symbols to inspect. * @param {Array} chrSymbols The character symbols to find. * @returns {number} Returns the index of the last unmatched string symbol. */ function charsEndIndex(strSymbols, chrSymbols) { var index = strSymbols.length; while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} return index; } /** * Gets the number of `placeholder` occurrences in `array`. * * @private * @param {Array} array The array to inspect. * @param {*} placeholder The placeholder to search for. * @returns {number} Returns the placeholder count. */ function countHolders(array, placeholder) { var length = array.length, result = 0; while (length--) { if (array[length] === placeholder) { ++result; } } return result; } /** * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A * letters to basic Latin letters. * * @private * @param {string} letter The matched letter to deburr. * @returns {string} Returns the deburred letter. */ var deburrLetter = basePropertyOf(deburredLetters); /** * Used by `_.escape` to convert characters to HTML entities. * * @private * @param {string} chr The matched character to escape. * @returns {string} Returns the escaped character. */ var escapeHtmlChar = basePropertyOf(htmlEscapes); /** * Used by `_.template` to escape characters for inclusion in compiled string literals. * * @private * @param {string} chr The matched character to escape. * @returns {string} Returns the escaped character. */ function escapeStringChar(chr) { return '\\' + stringEscapes[chr]; } /** * Gets the value at `key` of `object`. * * @private * @param {Object} [object] The object to query. * @param {string} key The key of the property to get. * @returns {*} Returns the property value. */ function getValue(object, key) { return object == null ? undefined : object[key]; } /** * Checks if `string` contains Unicode symbols. * * @private * @param {string} string The string to inspect. * @returns {boolean} Returns `true` if a symbol is found, else `false`. */ function hasUnicode(string) { return reHasUnicode.test(string); } /** * Checks if `string` contains a word composed of Unicode symbols. * * @private * @param {string} string The string to inspect. * @returns {boolean} Returns `true` if a word is found, else `false`. */ function hasUnicodeWord(string) { return reHasUnicodeWord.test(string); } /** * Converts `iterator` to an array. * * @private * @param {Object} iterator The iterator to convert. * @returns {Array} Returns the converted array. */ function iteratorToArray(iterator) { var data, result = []; while (!(data = iterator.next()).done) { result.push(data.value); } return result; } /** * Converts `map` to its key-value pairs. * * @private * @param {Object} map The map to convert. * @returns {Array} Returns the key-value pairs. */ function mapToArray(map) { var index = -1, result = Array(map.size); map.forEach(function(value, key) { result[++index] = [key, value]; }); return result; } /** * Creates a unary function that invokes `func` with its argument transformed. * * @private * @param {Function} func The function to wrap. * @param {Function} transform The argument transform. * @returns {Function} Returns the new function. */ function overArg(func, transform) { return function(arg) { return func(transform(arg)); }; } /** * Replaces all `placeholder` elements in `array` with an internal placeholder * and returns an array of their indexes. * * @private * @param {Array} array The array to modify. * @param {*} placeholder The placeholder to replace. * @returns {Array} Returns the new array of placeholder indexes. */ function replaceHolders(array, placeholder) { var index = -1, length = array.length, resIndex = 0, result = []; while (++index < length) { var value = array[index]; if (value === placeholder || value === PLACEHOLDER) { array[index] = PLACEHOLDER; result[resIndex++] = index; } } return result; } /** * Gets the value at `key`, unless `key` is "__proto__". * * @private * @param {Object} object The object to query. * @param {string} key The key of the property to get. * @returns {*} Returns the property value. */ function safeGet(object, key) { return key == '__proto__' ? undefined : object[key]; } /** * Converts `set` to an array of its values. * * @private * @param {Object} set The set to convert. * @returns {Array} Returns the values. */ function setToArray(set) { var index = -1, result = Array(set.size); set.forEach(function(value) { result[++index] = value; }); return result; } /** * Converts `set` to its value-value pairs. * * @private * @param {Object} set The set to convert. * @returns {Array} Returns the value-value pairs. */ function setToPairs(set) { var index = -1, result = Array(set.size); set.forEach(function(value) { result[++index] = [value, value]; }); return result; } /** * A specialized version of `_.indexOf` which performs strict equality * comparisons of values, i.e. `===`. * * @private * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} fromIndex The index to search from. * @returns {number} Returns the index of the matched value, else `-1`. */ function strictIndexOf(array, value, fromIndex) { var index = fromIndex - 1, length = array.length; while (++index < length) { if (array[index] === value) { return index; } } return -1; } /** * A specialized version of `_.lastIndexOf` which performs strict equality * comparisons of values, i.e. `===`. * * @private * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} fromIndex The index to search from. * @returns {number} Returns the index of the matched value, else `-1`. */ function strictLastIndexOf(array, value, fromIndex) { var index = fromIndex + 1; while (index--) { if (array[index] === value) { return index; } } return index; } /** * Gets the number of symbols in `string`. * * @private * @param {string} string The string to inspect. * @returns {number} Returns the string size. */ function stringSize(string) { return hasUnicode(string) ? unicodeSize(string) : asciiSize(string); } /** * Converts `string` to an array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the converted array. */ function stringToArray(string) { return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string); } /** * Used by `_.unescape` to convert HTML entities to characters. * * @private * @param {string} chr The matched character to unescape. * @returns {string} Returns the unescaped character. */ var unescapeHtmlChar = basePropertyOf(htmlUnescapes); /** * Gets the size of a Unicode `string`. * * @private * @param {string} string The string inspect. * @returns {number} Returns the string size. */ function unicodeSize(string) { var result = reUnicode.lastIndex = 0; while (reUnicode.test(string)) { ++result; } return result; } /** * Converts a Unicode `string` to an array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the converted array. */ function unicodeToArray(string) { return string.match(reUnicode) || []; } /** * Splits a Unicode `string` into an array of its words. * * @private * @param {string} The string to inspect. * @returns {Array} Returns the words of `string`. */ function unicodeWords(string) { return string.match(reUnicodeWord) || []; } /*--------------------------------------------------------------------------*/ /** * Create a new pristine `lodash` function using the `context` object. * * @static * @memberOf _ * @since 1.1.0 * @category Util * @param {Object} [context=root] The context object. * @returns {Function} Returns a new `lodash` function. * @example * * _.mixin({ 'foo': _.constant('foo') }); * * var lodash = _.runInContext(); * lodash.mixin({ 'bar': lodash.constant('bar') }); * * _.isFunction(_.foo); * // => true * _.isFunction(_.bar); * // => false * * lodash.isFunction(lodash.foo); * // => false * lodash.isFunction(lodash.bar); * // => true * * // Create a suped-up `defer` in Node.js. * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; */ var runInContext = (function runInContext(context) { context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps)); /** Built-in constructor references. */ var Array = context.Array, Date = context.Date, Error = context.Error, Function = context.Function, Math = context.Math, Object = context.Object, RegExp = context.RegExp, String = context.String, TypeError = context.TypeError; /** Used for built-in method references. */ var arrayProto = Array.prototype, funcProto = Function.prototype, objectProto = Object.prototype; /** Used to detect overreaching core-js shims. */ var coreJsData = context['__core-js_shared__']; /** Used to resolve the decompiled source of functions. */ var funcToString = funcProto.toString; /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; /** Used to generate unique IDs. */ var idCounter = 0; /** Used to detect methods masquerading as native. */ var maskSrcKey = (function() { var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); return uid ? ('Symbol(src)_1.' + uid) : ''; }()); /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var nativeObjectToString = objectProto.toString; /** Used to infer the `Object` constructor. */ var objectCtorString = funcToString.call(Object); /** Used to restore the original `_` reference in `_.noConflict`. */ var oldDash = root._; /** Used to detect if a method is native. */ var reIsNative = RegExp('^' + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); /** Built-in value references. */ var Buffer = moduleExports ? context.Buffer : undefined, Symbol = context.Symbol, Uint8Array = context.Uint8Array, allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined, getPrototype = overArg(Object.getPrototypeOf, Object), objectCreate = Object.create, propertyIsEnumerable = objectProto.propertyIsEnumerable, splice = arrayProto.splice, spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined, symIterator = Symbol ? Symbol.iterator : undefined, symToStringTag = Symbol ? Symbol.toStringTag : undefined; var defineProperty = (function() { try { var func = getNative(Object, 'defineProperty'); func({}, '', {}); return func; } catch (e) {} }()); /** Mocked built-ins. */ var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout, ctxNow = Date && Date.now !== root.Date.now && Date.now, ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout; /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeCeil = Math.ceil, nativeFloor = Math.floor, nativeGetSymbols = Object.getOwnPropertySymbols, nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined, nativeIsFinite = context.isFinite, nativeJoin = arrayProto.join, nativeKeys = overArg(Object.keys, Object), nativeMax = Math.max, nativeMin = Math.min, nativeNow = Date.now, nativeParseInt = context.parseInt, nativeRandom = Math.random, nativeReverse = arrayProto.reverse; /* Built-in method references that are verified to be native. */ var DataView = getNative(context, 'DataView'), Map = getNative(context, 'Map'), Promise = getNative(context, 'Promise'), Set = getNative(context, 'Set'), WeakMap = getNative(context, 'WeakMap'), nativeCreate = getNative(Object, 'create'); /** Used to store function metadata. */ var metaMap = WeakMap && new WeakMap; /** Used to lookup unminified function names. */ var realNames = {}; /** Used to detect maps, sets, and weakmaps. */ var dataViewCtorString = toSource(DataView), mapCtorString = toSource(Map), promiseCtorString = toSource(Promise), setCtorString = toSource(Set), weakMapCtorString = toSource(WeakMap); /** Used to convert symbols to primitives and strings. */ var symbolProto = Symbol ? Symbol.prototype : undefined, symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, symbolToString = symbolProto ? symbolProto.toString : undefined; /*------------------------------------------------------------------------*/ /** * Creates a `lodash` object which wraps `value` to enable implicit method * chain sequences. Methods that operate on and return arrays, collections, * and functions can be chained together. Methods that retrieve a single value * or may return a primitive value will automatically end the chain sequence * and return the unwrapped value. Otherwise, the value must be unwrapped * with `_#value`. * * Explicit chain sequences, which must be unwrapped with `_#value`, may be * enabled using `_.chain`. * * The execution of chained methods is lazy, that is, it's deferred until * `_#value` is implicitly or explicitly called. * * Lazy evaluation allows several methods to support shortcut fusion. * Shortcut fusion is an optimization to merge iteratee calls; this avoids * the creation of intermediate arrays and can greatly reduce the number of * iteratee executions. Sections of a chain sequence qualify for shortcut * fusion if the section is applied to an array and iteratees accept only * one argument. The heuristic for whether a section qualifies for shortcut * fusion is subject to change. * * Chaining is supported in custom builds as long as the `_#value` method is * directly or indirectly included in the build. * * In addition to lodash methods, wrappers have `Array` and `String` methods. * * The wrapper `Array` methods are: * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift` * * The wrapper `String` methods are: * `replace` and `split` * * The wrapper methods that support shortcut fusion are: * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`, * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`, * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray` * * The chainable wrapper methods are: * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`, * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`, * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`, * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`, * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`, * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`, * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`, * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`, * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`, * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`, * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`, * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`, * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`, * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`, * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`, * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`, * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`, * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`, * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`, * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`, * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`, * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`, * `zipObject`, `zipObjectDeep`, and `zipWith` * * The wrapper methods that are **not** chainable by default are: * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`, * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`, * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`, * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`, * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`, * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`, * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`, * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`, * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`, * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`, * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`, * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`, * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`, * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`, * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`, * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`, * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`, * `upperFirst`, `value`, and `words` * * @name _ * @constructor * @category Seq * @param {*} value The value to wrap in a `lodash` instance. * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * function square(n) { * return n * n; * } * * var wrapped = _([1, 2, 3]); * * // Returns an unwrapped value. * wrapped.reduce(_.add); * // => 6 * * // Returns a wrapped value. * var squares = wrapped.map(square); * * _.isArray(squares); * // => false * * _.isArray(squares.value()); * // => true */ function lodash(value) { if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { if (value instanceof LodashWrapper) { return value; } if (hasOwnProperty.call(value, '__wrapped__')) { return wrapperClone(value); } } return new LodashWrapper(value); } /** * The base implementation of `_.create` without support for assigning * properties to the created object. * * @private * @param {Object} proto The object to inherit from. * @returns {Object} Returns the new object. */ var baseCreate = (function() { function object() {} return function(proto) { if (!isObject(proto)) { return {}; } if (objectCreate) { return objectCreate(proto); } object.prototype = proto; var result = new object; object.prototype = undefined; return result; }; }()); /** * The function whose prototype chain sequence wrappers inherit from. * * @private */ function baseLodash() { // No operation performed. } /** * The base constructor for creating `lodash` wrapper objects. * * @private * @param {*} value The value to wrap. * @param {boolean} [chainAll] Enable explicit method chain sequences. */ function LodashWrapper(value, chainAll) { this.__wrapped__ = value; this.__actions__ = []; this.__chain__ = !!chainAll; this.__index__ = 0; this.__values__ = undefined; } /** * By default, the template delimiters used by lodash are like those in * embedded Ruby (ERB) as well as ES2015 template strings. Change the * following template settings to use alternative delimiters. * * @static * @memberOf _ * @type {Object} */ lodash.templateSettings = { /** * Used to detect `data` property values to be HTML-escaped. * * @memberOf _.templateSettings * @type {RegExp} */ 'escape': reEscape, /** * Used to detect code to be evaluated. * * @memberOf _.templateSettings * @type {RegExp} */ 'evaluate': reEvaluate, /** * Used to detect `data` property values to inject. * * @memberOf _.templateSettings * @type {RegExp} */ 'interpolate': reInterpolate, /** * Used to reference the data object in the template text. * * @memberOf _.templateSettings * @type {string} */ 'variable': '', /** * Used to import variables into the compiled template. * * @memberOf _.templateSettings * @type {Object} */ 'imports': { /** * A reference to the `lodash` function. * * @memberOf _.templateSettings.imports * @type {Function} */ '_': lodash } }; // Ensure wrappers are instances of `baseLodash`. lodash.prototype = baseLodash.prototype; lodash.prototype.constructor = lodash; LodashWrapper.prototype = baseCreate(baseLodash.prototype); LodashWrapper.prototype.constructor = LodashWrapper; /*------------------------------------------------------------------------*/ /** * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. * * @private * @constructor * @param {*} value The value to wrap. */ function LazyWrapper(value) { this.__wrapped__ = value; this.__actions__ = []; this.__dir__ = 1; this.__filtered__ = false; this.__iteratees__ = []; this.__takeCount__ = MAX_ARRAY_LENGTH; this.__views__ = []; } /** * Creates a clone of the lazy wrapper object. * * @private * @name clone * @memberOf LazyWrapper * @returns {Object} Returns the cloned `LazyWrapper` object. */ function lazyClone() { var result = new LazyWrapper(this.__wrapped__); result.__actions__ = copyArray(this.__actions__); result.__dir__ = this.__dir__; result.__filtered__ = this.__filtered__; result.__iteratees__ = copyArray(this.__iteratees__); result.__takeCount__ = this.__takeCount__; result.__views__ = copyArray(this.__views__); return result; } /** * Reverses the direction of lazy iteration. * * @private * @name reverse * @memberOf LazyWrapper * @returns {Object} Returns the new reversed `LazyWrapper` object. */ function lazyReverse() { if (this.__filtered__) { var result = new LazyWrapper(this); result.__dir__ = -1; result.__filtered__ = true; } else { result = this.clone(); result.__dir__ *= -1; } return result; } /** * Extracts the unwrapped value from its lazy wrapper. * * @private * @name value * @memberOf LazyWrapper * @returns {*} Returns the unwrapped value. */ function lazyValue() { var array = this.__wrapped__.value(), dir = this.__dir__, isArr = isArray(array), isRight = dir < 0, arrLength = isArr ? array.length : 0, view = getView(0, arrLength, this.__views__), start = view.start, end = view.end, length = end - start, index = isRight ? end : (start - 1), iteratees = this.__iteratees__, iterLength = iteratees.length, resIndex = 0, takeCount = nativeMin(length, this.__takeCount__); if (!isArr || (!isRight && arrLength == length && takeCount == length)) { return baseWrapperValue(array, this.__actions__); } var result = []; outer: while (length-- && resIndex < takeCount) { index += dir; var iterIndex = -1, value = array[index]; while (++iterIndex < iterLength) { var data = iteratees[iterIndex], iteratee = data.iteratee, type = data.type, computed = iteratee(value); if (type == LAZY_MAP_FLAG) { value = computed; } else if (!computed) { if (type == LAZY_FILTER_FLAG) { continue outer; } else { break outer; } } } result[resIndex++] = value; } return result; } // Ensure `LazyWrapper` is an instance of `baseLodash`. LazyWrapper.prototype = baseCreate(baseLodash.prototype); LazyWrapper.prototype.constructor = LazyWrapper; /*------------------------------------------------------------------------*/ /** * Creates a hash object. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function Hash(entries) { var index = -1, length = entries == null ? 0 : entries.length; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the hash. * * @private * @name clear * @memberOf Hash */ function hashClear() { this.__data__ = nativeCreate ? nativeCreate(null) : {}; this.size = 0; } /** * Removes `key` and its value from the hash. * * @private * @name delete * @memberOf Hash * @param {Object} hash The hash to modify. * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function hashDelete(key) { var result = this.has(key) && delete this.__data__[key]; this.size -= result ? 1 : 0; return result; } /** * Gets the hash value for `key`. * * @private * @name get * @memberOf Hash * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function hashGet(key) { var data = this.__data__; if (nativeCreate) { var result = data[key]; return result === HASH_UNDEFINED ? undefined : result; } return hasOwnProperty.call(data, key) ? data[key] : undefined; } /** * Checks if a hash value for `key` exists. * * @private * @name has * @memberOf Hash * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function hashHas(key) { var data = this.__data__; return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key); } /** * Sets the hash `key` to `value`. * * @private * @name set * @memberOf Hash * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the hash instance. */ function hashSet(key, value) { var data = this.__data__; this.size += this.has(key) ? 0 : 1; data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; return this; } // Add methods to `Hash`. Hash.prototype.clear = hashClear; Hash.prototype['delete'] = hashDelete; Hash.prototype.get = hashGet; Hash.prototype.has = hashHas; Hash.prototype.set = hashSet; /*------------------------------------------------------------------------*/ /** * Creates an list cache object. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function ListCache(entries) { var index = -1, length = entries == null ? 0 : entries.length; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the list cache. * * @private * @name clear * @memberOf ListCache */ function listCacheClear() { this.__data__ = []; this.size = 0; } /** * Removes `key` and its value from the list cache. * * @private * @name delete * @memberOf ListCache * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function listCacheDelete(key) { var data = this.__data__, index = assocIndexOf(data, key); if (index < 0) { return false; } var lastIndex = data.length - 1; if (index == lastIndex) { data.pop(); } else { splice.call(data, index, 1); } --this.size; return true; } /** * Gets the list cache value for `key`. * * @private * @name get * @memberOf ListCache * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function listCacheGet(key) { var data = this.__data__, index = assocIndexOf(data, key); return index < 0 ? undefined : data[index][1]; } /** * Checks if a list cache value for `key` exists. * * @private * @name has * @memberOf ListCache * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function listCacheHas(key) { return assocIndexOf(this.__data__, key) > -1; } /** * Sets the list cache `key` to `value`. * * @private * @name set * @memberOf ListCache * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the list cache instance. */ function listCacheSet(key, value) { var data = this.__data__, index = assocIndexOf(data, key); if (index < 0) { ++this.size; data.push([key, value]); } else { data[index][1] = value; } return this; } // Add methods to `ListCache`. ListCache.prototype.clear = listCacheClear; ListCache.prototype['delete'] = listCacheDelete; ListCache.prototype.get = listCacheGet; ListCache.prototype.has = listCacheHas; ListCache.prototype.set = listCacheSet; /*------------------------------------------------------------------------*/ /** * Creates a map cache object to store key-value pairs. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function MapCache(entries) { var index = -1, length = entries == null ? 0 : entries.length; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the map. * * @private * @name clear * @memberOf MapCache */ function mapCacheClear() { this.size = 0; this.__data__ = { 'hash': new Hash, 'map': new (Map || ListCache), 'string': new Hash }; } /** * Removes `key` and its value from the map. * * @private * @name delete * @memberOf MapCache * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function mapCacheDelete(key) { var result = getMapData(this, key)['delete'](key); this.size -= result ? 1 : 0; return result; } /** * Gets the map value for `key`. * * @private * @name get * @memberOf MapCache * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function mapCacheGet(key) { return getMapData(this, key).get(key); } /** * Checks if a map value for `key` exists. * * @private * @name has * @memberOf MapCache * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function mapCacheHas(key) { return getMapData(this, key).has(key); } /** * Sets the map `key` to `value`. * * @private * @name set * @memberOf MapCache * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the map cache instance. */ function mapCacheSet(key, value) { var data = getMapData(this, key), size = data.size; data.set(key, value); this.size += data.size == size ? 0 : 1; return this; } // Add methods to `MapCache`. MapCache.prototype.clear = mapCacheClear; MapCache.prototype['delete'] = mapCacheDelete; MapCache.prototype.get = mapCacheGet; MapCache.prototype.has = mapCacheHas; MapCache.prototype.set = mapCacheSet; /*------------------------------------------------------------------------*/ /** * * Creates an array cache object to store unique values. * * @private * @constructor * @param {Array} [values] The values to cache. */ function SetCache(values) { var index = -1, length = values == null ? 0 : values.length; this.__data__ = new MapCache; while (++index < length) { this.add(values[index]); } } /** * Adds `value` to the array cache. * * @private * @name add * @memberOf SetCache * @alias push * @param {*} value The value to cache. * @returns {Object} Returns the cache instance. */ function setCacheAdd(value) { this.__data__.set(value, HASH_UNDEFINED); return this; } /** * Checks if `value` is in the array cache. * * @private * @name has * @memberOf SetCache * @param {*} value The value to search for. * @returns {number} Returns `true` if `value` is found, else `false`. */ function setCacheHas(value) { return this.__data__.has(value); } // Add methods to `SetCache`. SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; SetCache.prototype.has = setCacheHas; /*------------------------------------------------------------------------*/ /** * Creates a stack cache object to store key-value pairs. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function Stack(entries) { var data = this.__data__ = new ListCache(entries); this.size = data.size; } /** * Removes all key-value entries from the stack. * * @private * @name clear * @memberOf Stack */ function stackClear() { this.__data__ = new ListCache; this.size = 0; } /** * Removes `key` and its value from the stack. * * @private * @name delete * @memberOf Stack * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function stackDelete(key) { var data = this.__data__, result = data['delete'](key); this.size = data.size; return result; } /** * Gets the stack value for `key`. * * @private * @name get * @memberOf Stack * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function stackGet(key) { return this.__data__.get(key); } /** * Checks if a stack value for `key` exists. * * @private * @name has * @memberOf Stack * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function stackHas(key) { return this.__data__.has(key); } /** * Sets the stack `key` to `value`. * * @private * @name set * @memberOf Stack * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the stack cache instance. */ function stackSet(key, value) { var data = this.__data__; if (data instanceof ListCache) { var pairs = data.__data__; if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { pairs.push([key, value]); this.size = ++data.size; return this; } data = this.__data__ = new MapCache(pairs); } data.set(key, value); this.size = data.size; return this; } // Add methods to `Stack`. Stack.prototype.clear = stackClear; Stack.prototype['delete'] = stackDelete; Stack.prototype.get = stackGet; Stack.prototype.has = stackHas; Stack.prototype.set = stackSet; /*------------------------------------------------------------------------*/ /** * Creates an array of the enumerable property names of the array-like `value`. * * @private * @param {*} value The value to query. * @param {boolean} inherited Specify returning inherited property names. * @returns {Array} Returns the array of property names. */ function arrayLikeKeys(value, inherited) { var isArr = isArray(value), isArg = !isArr && isArguments(value), isBuff = !isArr && !isArg && isBuffer(value), isType = !isArr && !isArg && !isBuff && isTypedArray(value), skipIndexes = isArr || isArg || isBuff || isType, result = skipIndexes ? baseTimes(value.length, String) : [], length = result.length; for (var key in value) { if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && ( // Safari 9 has enumerable `arguments.length` in strict mode. key == 'length' || // Node.js 0.10 has enumerable non-index properties on buffers. (isBuff && (key == 'offset' || key == 'parent')) || // PhantomJS 2 has enumerable non-index properties on typed arrays. (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) || // Skip index properties. isIndex(key, length) ))) { result.push(key); } } return result; } /** * A specialized version of `_.sample` for arrays. * * @private * @param {Array} array The array to sample. * @returns {*} Returns the random element. */ function arraySample(array) { var length = array.length; return length ? array[baseRandom(0, length - 1)] : undefined; } /** * A specialized version of `_.sampleSize` for arrays. * * @private * @param {Array} array The array to sample. * @param {number} n The number of elements to sample. * @returns {Array} Returns the random elements. */ function arraySampleSize(array, n) { return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length)); } /** * A specialized version of `_.shuffle` for arrays. * * @private * @param {Array} array The array to shuffle. * @returns {Array} Returns the new shuffled array. */ function arrayShuffle(array) { return shuffleSelf(copyArray(array)); } /** * This function is like `assignValue` except that it doesn't assign * `undefined` values. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ function assignMergeValue(object, key, value) { if ((value !== undefined && !eq(object[key], value)) || (value === undefined && !(key in object))) { baseAssignValue(object, key, value); } } /** * Assigns `value` to `key` of `object` if the existing value is not equivalent * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ function assignValue(object, key, value) { var objValue = object[key]; if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || (value === undefined && !(key in object))) { baseAssignValue(object, key, value); } } /** * Gets the index at which the `key` is found in `array` of key-value pairs. * * @private * @param {Array} array The array to inspect. * @param {*} key The key to search for. * @returns {number} Returns the index of the matched value, else `-1`. */ function assocIndexOf(array, key) { var length = array.length; while (length--) { if (eq(array[length][0], key)) { return length; } } return -1; } /** * Aggregates elements of `collection` on `accumulator` with keys transformed * by `iteratee` and values set by `setter`. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} setter The function to set `accumulator` values. * @param {Function} iteratee The iteratee to transform keys. * @param {Object} accumulator The initial aggregated object. * @returns {Function} Returns `accumulator`. */ function baseAggregator(collection, setter, iteratee, accumulator) { baseEach(collection, function(value, key, collection) { setter(accumulator, value, iteratee(value), collection); }); return accumulator; } /** * The base implementation of `_.assign` without support for multiple sources * or `customizer` functions. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @returns {Object} Returns `object`. */ function baseAssign(object, source) { return object && copyObject(source, keys(source), object); } /** * The base implementation of `_.assignIn` without support for multiple sources * or `customizer` functions. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @returns {Object} Returns `object`. */ function baseAssignIn(object, source) { return object && copyObject(source, keysIn(source), object); } /** * The base implementation of `assignValue` and `assignMergeValue` without * value checks. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ function baseAssignValue(object, key, value) { if (key == '__proto__' && defineProperty) { defineProperty(object, key, { 'configurable': true, 'enumerable': true, 'value': value, 'writable': true }); } else { object[key] = value; } } /** * The base implementation of `_.at` without support for individual paths. * * @private * @param {Object} object The object to iterate over. * @param {string[]} paths The property paths to pick. * @returns {Array} Returns the picked elements. */ function baseAt(object, paths) { var index = -1, length = paths.length, result = Array(length), skip = object == null; while (++index < length) { result[index] = skip ? undefined : get(object, paths[index]); } return result; } /** * The base implementation of `_.clamp` which doesn't coerce arguments. * * @private * @param {number} number The number to clamp. * @param {number} [lower] The lower bound. * @param {number} upper The upper bound. * @returns {number} Returns the clamped number. */ function baseClamp(number, lower, upper) { if (number === number) { if (upper !== undefined) { number = number <= upper ? number : upper; } if (lower !== undefined) { number = number >= lower ? number : lower; } } return number; } /** * The base implementation of `_.clone` and `_.cloneDeep` which tracks * traversed objects. * * @private * @param {*} value The value to clone. * @param {boolean} bitmask The bitmask flags. * 1 - Deep clone * 2 - Flatten inherited properties * 4 - Clone symbols * @param {Function} [customizer] The function to customize cloning. * @param {string} [key] The key of `value`. * @param {Object} [object] The parent object of `value`. * @param {Object} [stack] Tracks traversed objects and their clone counterparts. * @returns {*} Returns the cloned value. */ function baseClone(value, bitmask, customizer, key, object, stack) { var result, isDeep = bitmask & CLONE_DEEP_FLAG, isFlat = bitmask & CLONE_FLAT_FLAG, isFull = bitmask & CLONE_SYMBOLS_FLAG; if (customizer) { result = object ? customizer(value, key, object, stack) : customizer(value); } if (result !== undefined) { return result; } if (!isObject(value)) { return value; } var isArr = isArray(value); if (isArr) { result = initCloneArray(value); if (!isDeep) { return copyArray(value, result); } } else { var tag = getTag(value), isFunc = tag == funcTag || tag == genTag; if (isBuffer(value)) { return cloneBuffer(value, isDeep); } if (tag == objectTag || tag == argsTag || (isFunc && !object)) { result = (isFlat || isFunc) ? {} : initCloneObject(value); if (!isDeep) { return isFlat ? copySymbolsIn(value, baseAssignIn(result, value)) : copySymbols(value, baseAssign(result, value)); } } else { if (!cloneableTags[tag]) { return object ? value : {}; } result = initCloneByTag(value, tag, isDeep); } } // Check for circular references and return its corresponding clone. stack || (stack = new Stack); var stacked = stack.get(value); if (stacked) { return stacked; } stack.set(value, result); if (isSet(value)) { value.forEach(function(subValue) { result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack)); }); return result; } if (isMap(value)) { value.forEach(function(subValue, key) { result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack)); }); return result; } var keysFunc = isFull ? (isFlat ? getAllKeysIn : getAllKeys) : (isFlat ? keysIn : keys); var props = isArr ? undefined : keysFunc(value); arrayEach(props || value, function(subValue, key) { if (props) { key = subValue; subValue = value[key]; } // Recursively populate clone (susceptible to call stack limits). assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)); }); return result; } /** * The base implementation of `_.conforms` which doesn't clone `source`. * * @private * @param {Object} source The object of property predicates to conform to. * @returns {Function} Returns the new spec function. */ function baseConforms(source) { var props = keys(source); return function(object) { return baseConformsTo(object, source, props); }; } /** * The base implementation of `_.conformsTo` which accepts `props` to check. * * @private * @param {Object} object The object to inspect. * @param {Object} source The object of property predicates to conform to. * @returns {boolean} Returns `true` if `object` conforms, else `false`. */ function baseConformsTo(object, source, props) { var length = props.length; if (object == null) { return !length; } object = Object(object); while (length--) { var key = props[length], predicate = source[key], value = object[key]; if ((value === undefined && !(key in object)) || !predicate(value)) { return false; } } return true; } /** * The base implementation of `_.delay` and `_.defer` which accepts `args` * to provide to `func`. * * @private * @param {Function} func The function to delay. * @param {number} wait The number of milliseconds to delay invocation. * @param {Array} args The arguments to provide to `func`. * @returns {number|Object} Returns the timer id or timeout object. */ function baseDelay(func, wait, args) { if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } return setTimeout(function() { func.apply(undefined, args); }, wait); } /** * The base implementation of methods like `_.difference` without support * for excluding multiple arrays or iteratee shorthands. * * @private * @param {Array} array The array to inspect. * @param {Array} values The values to exclude. * @param {Function} [iteratee] The iteratee invoked per element. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of filtered values. */ function baseDifference(array, values, iteratee, comparator) { var index = -1, includes = arrayIncludes, isCommon = true, length = array.length, result = [], valuesLength = values.length; if (!length) { return result; } if (iteratee) { values = arrayMap(values, baseUnary(iteratee)); } if (comparator) { includes = arrayIncludesWith; isCommon = false; } else if (values.length >= LARGE_ARRAY_SIZE) { includes = cacheHas; isCommon = false; values = new SetCache(values); } outer: while (++index < length) { var value = array[index], computed = iteratee == null ? value : iteratee(value); value = (comparator || value !== 0) ? value : 0; if (isCommon && computed === computed) { var valuesIndex = valuesLength; while (valuesIndex--) { if (values[valuesIndex] === computed) { continue outer; } } result.push(value); } else if (!includes(values, computed, comparator)) { result.push(value); } } return result; } /** * The base implementation of `_.forEach` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array|Object} Returns `collection`. */ var baseEach = createBaseEach(baseForOwn); /** * The base implementation of `_.forEachRight` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array|Object} Returns `collection`. */ var baseEachRight = createBaseEach(baseForOwnRight, true); /** * The base implementation of `_.every` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if all elements pass the predicate check, * else `false` */ function baseEvery(collection, predicate) { var result = true; baseEach(collection, function(value, index, collection) { result = !!predicate(value, index, collection); return result; }); return result; } /** * The base implementation of methods like `_.max` and `_.min` which accepts a * `comparator` to determine the extremum value. * * @private * @param {Array} array The array to iterate over. * @param {Function} iteratee The iteratee invoked per iteration. * @param {Function} comparator The comparator used to compare values. * @returns {*} Returns the extremum value. */ function baseExtremum(array, iteratee, comparator) { var index = -1, length = array.length; while (++index < length) { var value = array[index], current = iteratee(value); if (current != null && (computed === undefined ? (current === current && !isSymbol(current)) : comparator(current, computed) )) { var computed = current, result = value; } } return result; } /** * The base implementation of `_.fill` without an iteratee call guard. * * @private * @param {Array} array The array to fill. * @param {*} value The value to fill `array` with. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns `array`. */ function baseFill(array, value, start, end) { var length = array.length; start = toInteger(start); if (start < 0) { start = -start > length ? 0 : (length + start); } end = (end === undefined || end > length) ? length : toInteger(end); if (end < 0) { end += length; } end = start > end ? 0 : toLength(end); while (start < end) { array[start++] = value; } return array; } /** * The base implementation of `_.filter` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {Array} Returns the new filtered array. */ function baseFilter(collection, predicate) { var result = []; baseEach(collection, function(value, index, collection) { if (predicate(value, index, collection)) { result.push(value); } }); return result; } /** * The base implementation of `_.flatten` with support for restricting flattening. * * @private * @param {Array} array The array to flatten. * @param {number} depth The maximum recursion depth. * @param {boolean} [predicate=isFlattenable] The function invoked per iteration. * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. * @param {Array} [result=[]] The initial result value. * @returns {Array} Returns the new flattened array. */ function baseFlatten(array, depth, predicate, isStrict, result) { var index = -1, length = array.length; predicate || (predicate = isFlattenable); result || (result = []); while (++index < length) { var value = array[index]; if (depth > 0 && predicate(value)) { if (depth > 1) { // Recursively flatten arrays (susceptible to call stack limits). baseFlatten(value, depth - 1, predicate, isStrict, result); } else { arrayPush(result, value); } } else if (!isStrict) { result[result.length] = value; } } return result; } /** * The base implementation of `baseForOwn` which iterates over `object` * properties returned by `keysFunc` and invokes `iteratee` for each property. * Iteratee functions may exit iteration early by explicitly returning `false`. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {Function} keysFunc The function to get the keys of `object`. * @returns {Object} Returns `object`. */ var baseFor = createBaseFor(); /** * This function is like `baseFor` except that it iterates over properties * in the opposite order. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {Function} keysFunc The function to get the keys of `object`. * @returns {Object} Returns `object`. */ var baseForRight = createBaseFor(true); /** * The base implementation of `_.forOwn` without support for iteratee shorthands. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Object} Returns `object`. */ function baseForOwn(object, iteratee) { return object && baseFor(object, iteratee, keys); } /** * The base implementation of `_.forOwnRight` without support for iteratee shorthands. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Object} Returns `object`. */ function baseForOwnRight(object, iteratee) { return object && baseForRight(object, iteratee, keys); } /** * The base implementation of `_.functions` which creates an array of * `object` function property names filtered from `props`. * * @private * @param {Object} object The object to inspect. * @param {Array} props The property names to filter. * @returns {Array} Returns the function names. */ function baseFunctions(object, props) { return arrayFilter(props, function(key) { return isFunction(object[key]); }); } /** * The base implementation of `_.get` without support for default values. * * @private * @param {Object} object The object to query. * @param {Array|string} path The path of the property to get. * @returns {*} Returns the resolved value. */ function baseGet(object, path) { path = castPath(path, object); var index = 0, length = path.length; while (object != null && index < length) { object = object[toKey(path[index++])]; } return (index && index == length) ? object : undefined; } /** * The base implementation of `getAllKeys` and `getAllKeysIn` which uses * `keysFunc` and `symbolsFunc` to get the enumerable property names and * symbols of `object`. * * @private * @param {Object} object The object to query. * @param {Function} keysFunc The function to get the keys of `object`. * @param {Function} symbolsFunc The function to get the symbols of `object`. * @returns {Array} Returns the array of property names and symbols. */ function baseGetAllKeys(object, keysFunc, symbolsFunc) { var result = keysFunc(object); return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); } /** * The base implementation of `getTag` without fallbacks for buggy environments. * * @private * @param {*} value The value to query. * @returns {string} Returns the `toStringTag`. */ function baseGetTag(value) { if (value == null) { return value === undefined ? undefinedTag : nullTag; } return (symToStringTag && symToStringTag in Object(value)) ? getRawTag(value) : objectToString(value); } /** * The base implementation of `_.gt` which doesn't coerce arguments. * * @private * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if `value` is greater than `other`, * else `false`. */ function baseGt(value, other) { return value > other; } /** * The base implementation of `_.has` without support for deep paths. * * @private * @param {Object} [object] The object to query. * @param {Array|string} key The key to check. * @returns {boolean} Returns `true` if `key` exists, else `false`. */ function baseHas(object, key) { return object != null && hasOwnProperty.call(object, key); } /** * The base implementation of `_.hasIn` without support for deep paths. * * @private * @param {Object} [object] The object to query. * @param {Array|string} key The key to check. * @returns {boolean} Returns `true` if `key` exists, else `false`. */ function baseHasIn(object, key) { return object != null && key in Object(object); } /** * The base implementation of `_.inRange` which doesn't coerce arguments. * * @private * @param {number} number The number to check. * @param {number} start The start of the range. * @param {number} end The end of the range. * @returns {boolean} Returns `true` if `number` is in the range, else `false`. */ function baseInRange(number, start, end) { return number >= nativeMin(start, end) && number < nativeMax(start, end); } /** * The base implementation of methods like `_.intersection`, without support * for iteratee shorthands, that accepts an array of arrays to inspect. * * @private * @param {Array} arrays The arrays to inspect. * @param {Function} [iteratee] The iteratee invoked per element. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of shared values. */ function baseIntersection(arrays, iteratee, comparator) { var includes = comparator ? arrayIncludesWith : arrayIncludes, length = arrays[0].length, othLength = arrays.length, othIndex = othLength, caches = Array(othLength), maxLength = Infinity, result = []; while (othIndex--) { var array = arrays[othIndex]; if (othIndex && iteratee) { array = arrayMap(array, baseUnary(iteratee)); } maxLength = nativeMin(array.length, maxLength); caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120)) ? new SetCache(othIndex && array) : undefined; } array = arrays[0]; var index = -1, seen = caches[0]; outer: while (++index < length && result.length < maxLength) { var value = array[index], computed = iteratee ? iteratee(value) : value; value = (comparator || value !== 0) ? value : 0; if (!(seen ? cacheHas(seen, computed) : includes(result, computed, comparator) )) { othIndex = othLength; while (--othIndex) { var cache = caches[othIndex]; if (!(cache ? cacheHas(cache, computed) : includes(arrays[othIndex], computed, comparator)) ) { continue outer; } } if (seen) { seen.push(computed); } result.push(value); } } return result; } /** * The base implementation of `_.invert` and `_.invertBy` which inverts * `object` with values transformed by `iteratee` and set by `setter`. * * @private * @param {Object} object The object to iterate over. * @param {Function} setter The function to set `accumulator` values. * @param {Function} iteratee The iteratee to transform values. * @param {Object} accumulator The initial inverted object. * @returns {Function} Returns `accumulator`. */ function baseInverter(object, setter, iteratee, accumulator) { baseForOwn(object, function(value, key, object) { setter(accumulator, iteratee(value), key, object); }); return accumulator; } /** * The base implementation of `_.invoke` without support for individual * method arguments. * * @private * @param {Object} object The object to query. * @param {Array|string} path The path of the method to invoke. * @param {Array} args The arguments to invoke the method with. * @returns {*} Returns the result of the invoked method. */ function baseInvoke(object, path, args) { path = castPath(path, object); object = parent(object, path); var func = object == null ? object : object[toKey(last(path))]; return func == null ? undefined : apply(func, object, args); } /** * The base implementation of `_.isArguments`. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an `arguments` object, */ function baseIsArguments(value) { return isObjectLike(value) && baseGetTag(value) == argsTag; } /** * The base implementation of `_.isArrayBuffer` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. */ function baseIsArrayBuffer(value) { return isObjectLike(value) && baseGetTag(value) == arrayBufferTag; } /** * The base implementation of `_.isDate` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a date object, else `false`. */ function baseIsDate(value) { return isObjectLike(value) && baseGetTag(value) == dateTag; } /** * The base implementation of `_.isEqual` which supports partial comparisons * and tracks traversed objects. * * @private * @param {*} value The value to compare. * @param {*} other The other value to compare. * @param {boolean} bitmask The bitmask flags. * 1 - Unordered comparison * 2 - Partial comparison * @param {Function} [customizer] The function to customize comparisons. * @param {Object} [stack] Tracks traversed `value` and `other` objects. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. */ function baseIsEqual(value, other, bitmask, customizer, stack) { if (value === other) { return true; } if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) { return value !== value && other !== other; } return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack); } /** * A specialized version of `baseIsEqual` for arrays and objects which performs * deep comparisons and tracks traversed objects enabling objects with circular * references to be compared. * * @private * @param {Object} object The object to compare. * @param {Object} other The other object to compare. * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. * @param {Function} customizer The function to customize comparisons. * @param {Function} equalFunc The function to determine equivalents of values. * @param {Object} [stack] Tracks traversed `object` and `other` objects. * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. */ function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { var objIsArr = isArray(object), othIsArr = isArray(other), objTag = objIsArr ? arrayTag : getTag(object), othTag = othIsArr ? arrayTag : getTag(other); objTag = objTag == argsTag ? objectTag : objTag; othTag = othTag == argsTag ? objectTag : othTag; var objIsObj = objTag == objectTag, othIsObj = othTag == objectTag, isSameTag = objTag == othTag; if (isSameTag && isBuffer(object)) { if (!isBuffer(other)) { return false; } objIsArr = true; objIsObj = false; } if (isSameTag && !objIsObj) { stack || (stack = new Stack); return (objIsArr || isTypedArray(object)) ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack); } if (!(bitmask & COMPARE_PARTIAL_FLAG)) { var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); if (objIsWrapped || othIsWrapped) { var objUnwrapped = objIsWrapped ? object.value() : object, othUnwrapped = othIsWrapped ? other.value() : other; stack || (stack = new Stack); return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack); } } if (!isSameTag) { return false; } stack || (stack = new Stack); return equalObjects(object, other, bitmask, customizer, equalFunc, stack); } /** * The base implementation of `_.isMap` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a map, else `false`. */ function baseIsMap(value) { return isObjectLike(value) && getTag(value) == mapTag; } /** * The base implementation of `_.isMatch` without support for iteratee shorthands. * * @private * @param {Object} object The object to inspect. * @param {Object} source The object of property values to match. * @param {Array} matchData The property names, values, and compare flags to match. * @param {Function} [customizer] The function to customize comparisons. * @returns {boolean} Returns `true` if `object` is a match, else `false`. */ function baseIsMatch(object, source, matchData, customizer) { var index = matchData.length, length = index, noCustomizer = !customizer; if (object == null) { return !length; } object = Object(object); while (index--) { var data = matchData[index]; if ((noCustomizer && data[2]) ? data[1] !== object[data[0]] : !(data[0] in object) ) { return false; } } while (++index < length) { data = matchData[index]; var key = data[0], objValue = object[key], srcValue = data[1]; if (noCustomizer && data[2]) { if (objValue === undefined && !(key in object)) { return false; } } else { var stack = new Stack; if (customizer) { var result = customizer(objValue, srcValue, key, object, source, stack); } if (!(result === undefined ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) : result )) { return false; } } } return true; } /** * The base implementation of `_.isNative` without bad shim checks. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a native function, * else `false`. */ function baseIsNative(value) { if (!isObject(value) || isMasked(value)) { return false; } var pattern = isFunction(value) ? reIsNative : reIsHostCtor; return pattern.test(toSource(value)); } /** * The base implementation of `_.isRegExp` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. */ function baseIsRegExp(value) { return isObjectLike(value) && baseGetTag(value) == regexpTag; } /** * The base implementation of `_.isSet` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a set, else `false`. */ function baseIsSet(value) { return isObjectLike(value) && getTag(value) == setTag; } /** * The base implementation of `_.isTypedArray` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. */ function baseIsTypedArray(value) { return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; } /** * The base implementation of `_.iteratee`. * * @private * @param {*} [value=_.identity] The value to convert to an iteratee. * @returns {Function} Returns the iteratee. */ function baseIteratee(value) { // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. if (typeof value == 'function') { return value; } if (value == null) { return identity; } if (typeof value == 'object') { return isArray(value) ? baseMatchesProperty(value[0], value[1]) : baseMatches(value); } return property(value); } /** * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ function baseKeys(object) { if (!isPrototype(object)) { return nativeKeys(object); } var result = []; for (var key in Object(object)) { if (hasOwnProperty.call(object, key) && key != 'constructor') { result.push(key); } } return result; } /** * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ function baseKeysIn(object) { if (!isObject(object)) { return nativeKeysIn(object); } var isProto = isPrototype(object), result = []; for (var key in object) { if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { result.push(key); } } return result; } /** * The base implementation of `_.lt` which doesn't coerce arguments. * * @private * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if `value` is less than `other`, * else `false`. */ function baseLt(value, other) { return value < other; } /** * The base implementation of `_.map` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the new mapped array. */ function baseMap(collection, iteratee) { var index = -1, result = isArrayLike(collection) ? Array(collection.length) : []; baseEach(collection, function(value, key, collection) { result[++index] = iteratee(value, key, collection); }); return result; } /** * The base implementation of `_.matches` which doesn't clone `source`. * * @private * @param {Object} source The object of property values to match. * @returns {Function} Returns the new spec function. */ function baseMatches(source) { var matchData = getMatchData(source); if (matchData.length == 1 && matchData[0][2]) { return matchesStrictComparable(matchData[0][0], matchData[0][1]); } return function(object) { return object === source || baseIsMatch(object, source, matchData); }; } /** * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. * * @private * @param {string} path The path of the property to get. * @param {*} srcValue The value to match. * @returns {Function} Returns the new spec function. */ function baseMatchesProperty(path, srcValue) { if (isKey(path) && isStrictComparable(srcValue)) { return matchesStrictComparable(toKey(path), srcValue); } return function(object) { var objValue = get(object, path); return (objValue === undefined && objValue === srcValue) ? hasIn(object, path) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG); }; } /** * The base implementation of `_.merge` without support for multiple sources. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @param {number} srcIndex The index of `source`. * @param {Function} [customizer] The function to customize merged values. * @param {Object} [stack] Tracks traversed source values and their merged * counterparts. */ function baseMerge(object, source, srcIndex, customizer, stack) { if (object === source) { return; } baseFor(source, function(srcValue, key) { if (isObject(srcValue)) { stack || (stack = new Stack); baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); } else { var newValue = customizer ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack) : undefined; if (newValue === undefined) { newValue = srcValue; } assignMergeValue(object, key, newValue); } }, keysIn); } /** * A specialized version of `baseMerge` for arrays and objects which performs * deep merges and tracks traversed objects enabling objects with circular * references to be merged. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @param {string} key The key of the value to merge. * @param {number} srcIndex The index of `source`. * @param {Function} mergeFunc The function to merge values. * @param {Function} [customizer] The function to customize assigned values. * @param {Object} [stack] Tracks traversed source values and their merged * counterparts. */ function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { var objValue = safeGet(object, key), srcValue = safeGet(source, key), stacked = stack.get(srcValue); if (stacked) { assignMergeValue(object, key, stacked); return; } var newValue = customizer ? customizer(objValue, srcValue, (key + ''), object, source, stack) : undefined; var isCommon = newValue === undefined; if (isCommon) { var isArr = isArray(srcValue), isBuff = !isArr && isBuffer(srcValue), isTyped = !isArr && !isBuff && isTypedArray(srcValue); newValue = srcValue; if (isArr || isBuff || isTyped) { if (isArray(objValue)) { newValue = objValue; } else if (isArrayLikeObject(objValue)) { newValue = copyArray(objValue); } else if (isBuff) { isCommon = false; newValue = cloneBuffer(srcValue, true); } else if (isTyped) { isCommon = false; newValue = cloneTypedArray(srcValue, true); } else { newValue = []; } } else if (isPlainObject(srcValue) || isArguments(srcValue)) { newValue = objValue; if (isArguments(objValue)) { newValue = toPlainObject(objValue); } else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) { newValue = initCloneObject(srcValue); } } else { isCommon = false; } } if (isCommon) { // Recursively merge objects and arrays (susceptible to call stack limits). stack.set(srcValue, newValue); mergeFunc(newValue, srcValue, srcIndex, customizer, stack); stack['delete'](srcValue); } assignMergeValue(object, key, newValue); } /** * The base implementation of `_.nth` which doesn't coerce arguments. * * @private * @param {Array} array The array to query. * @param {number} n The index of the element to return. * @returns {*} Returns the nth element of `array`. */ function baseNth(array, n) { var length = array.length; if (!length) { return; } n += n < 0 ? length : 0; return isIndex(n, length) ? array[n] : undefined; } /** * The base implementation of `_.orderBy` without param guards. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. * @param {string[]} orders The sort orders of `iteratees`. * @returns {Array} Returns the new sorted array. */ function baseOrderBy(collection, iteratees, orders) { var index = -1; iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(getIteratee())); var result = baseMap(collection, function(value, key, collection) { var criteria = arrayMap(iteratees, function(iteratee) { return iteratee(value); }); return { 'criteria': criteria, 'index': ++index, 'value': value }; }); return baseSortBy(result, function(object, other) { return compareMultiple(object, other, orders); }); } /** * The base implementation of `_.pick` without support for individual * property identifiers. * * @private * @param {Object} object The source object. * @param {string[]} paths The property paths to pick. * @returns {Object} Returns the new object. */ function basePick(object, paths) { return basePickBy(object, paths, function(value, path) { return hasIn(object, path); }); } /** * The base implementation of `_.pickBy` without support for iteratee shorthands. * * @private * @param {Object} object The source object. * @param {string[]} paths The property paths to pick. * @param {Function} predicate The function invoked per property. * @returns {Object} Returns the new object. */ function basePickBy(object, paths, predicate) { var index = -1, length = paths.length, result = {}; while (++index < length) { var path = paths[index], value = baseGet(object, path); if (predicate(value, path)) { baseSet(result, castPath(path, object), value); } } return result; } /** * A specialized version of `baseProperty` which supports deep paths. * * @private * @param {Array|string} path The path of the property to get. * @returns {Function} Returns the new accessor function. */ function basePropertyDeep(path) { return function(object) { return baseGet(object, path); }; } /** * The base implementation of `_.pullAllBy` without support for iteratee * shorthands. * * @private * @param {Array} array The array to modify. * @param {Array} values The values to remove. * @param {Function} [iteratee] The iteratee invoked per element. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns `array`. */ function basePullAll(array, values, iteratee, comparator) { var indexOf = comparator ? baseIndexOfWith : baseIndexOf, index = -1, length = values.length, seen = array; if (array === values) { values = copyArray(values); } if (iteratee) { seen = arrayMap(array, baseUnary(iteratee)); } while (++index < length) { var fromIndex = 0, value = values[index], computed = iteratee ? iteratee(value) : value; while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) { if (seen !== array) { splice.call(seen, fromIndex, 1); } splice.call(array, fromIndex, 1); } } return array; } /** * The base implementation of `_.pullAt` without support for individual * indexes or capturing the removed elements. * * @private * @param {Array} array The array to modify. * @param {number[]} indexes The indexes of elements to remove. * @returns {Array} Returns `array`. */ function basePullAt(array, indexes) { var length = array ? indexes.length : 0, lastIndex = length - 1; while (length--) { var index = indexes[length]; if (length == lastIndex || index !== previous) { var previous = index; if (isIndex(index)) { splice.call(array, index, 1); } else { baseUnset(array, index); } } } return array; } /** * The base implementation of `_.random` without support for returning * floating-point numbers. * * @private * @param {number} lower The lower bound. * @param {number} upper The upper bound. * @returns {number} Returns the random number. */ function baseRandom(lower, upper) { return lower + nativeFloor(nativeRandom() * (upper - lower + 1)); } /** * The base implementation of `_.range` and `_.rangeRight` which doesn't * coerce arguments. * * @private * @param {number} start The start of the range. * @param {number} end The end of the range. * @param {number} step The value to increment or decrement by. * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Array} Returns the range of numbers. */ function baseRange(start, end, step, fromRight) { var index = -1, length = nativeMax(nativeCeil((end - start) / (step || 1)), 0), result = Array(length); while (length--) { result[fromRight ? length : ++index] = start; start += step; } return result; } /** * The base implementation of `_.repeat` which doesn't coerce arguments. * * @private * @param {string} string The string to repeat. * @param {number} n The number of times to repeat the string. * @returns {string} Returns the repeated string. */ function baseRepeat(string, n) { var result = ''; if (!string || n < 1 || n > MAX_SAFE_INTEGER) { return result; } // Leverage the exponentiation by squaring algorithm for a faster repeat. // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. do { if (n % 2) { result += string; } n = nativeFloor(n / 2); if (n) { string += string; } } while (n); return result; } /** * The base implementation of `_.rest` which doesn't validate or coerce arguments. * * @private * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @returns {Function} Returns the new function. */ function baseRest(func, start) { return setToString(overRest(func, start, identity), func + ''); } /** * The base implementation of `_.sample`. * * @private * @param {Array|Object} collection The collection to sample. * @returns {*} Returns the random element. */ function baseSample(collection) { return arraySample(values(collection)); } /** * The base implementation of `_.sampleSize` without param guards. * * @private * @param {Array|Object} collection The collection to sample. * @param {number} n The number of elements to sample. * @returns {Array} Returns the random elements. */ function baseSampleSize(collection, n) { var array = values(collection); return shuffleSelf(array, baseClamp(n, 0, array.length)); } /** * The base implementation of `_.set`. * * @private * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to set. * @param {*} value The value to set. * @param {Function} [customizer] The function to customize path creation. * @returns {Object} Returns `object`. */ function baseSet(object, path, value, customizer) { if (!isObject(object)) { return object; } path = castPath(path, object); var index = -1, length = path.length, lastIndex = length - 1, nested = object; while (nested != null && ++index < length) { var key = toKey(path[index]), newValue = value; if (index != lastIndex) { var objValue = nested[key]; newValue = customizer ? customizer(objValue, key, nested) : undefined; if (newValue === undefined) { newValue = isObject(objValue) ? objValue : (isIndex(path[index + 1]) ? [] : {}); } } assignValue(nested, key, newValue); nested = nested[key]; } return object; } /** * The base implementation of `setData` without support for hot loop shorting. * * @private * @param {Function} func The function to associate metadata with. * @param {*} data The metadata. * @returns {Function} Returns `func`. */ var baseSetData = !metaMap ? identity : function(func, data) { metaMap.set(func, data); return func; }; /** * The base implementation of `setToString` without support for hot loop shorting. * * @private * @param {Function} func The function to modify. * @param {Function} string The `toString` result. * @returns {Function} Returns `func`. */ var baseSetToString = !defineProperty ? identity : function(func, string) { return defineProperty(func, 'toString', { 'configurable': true, 'enumerable': false, 'value': constant(string), 'writable': true }); }; /** * The base implementation of `_.shuffle`. * * @private * @param {Array|Object} collection The collection to shuffle. * @returns {Array} Returns the new shuffled array. */ function baseShuffle(collection) { return shuffleSelf(values(collection)); } /** * The base implementation of `_.slice` without an iteratee call guard. * * @private * @param {Array} array The array to slice. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the slice of `array`. */ function baseSlice(array, start, end) { var index = -1, length = array.length; if (start < 0) { start = -start > length ? 0 : (length + start); } end = end > length ? length : end; if (end < 0) { end += length; } length = start > end ? 0 : ((end - start) >>> 0); start >>>= 0; var result = Array(length); while (++index < length) { result[index] = array[index + start]; } return result; } /** * The base implementation of `_.some` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if any element passes the predicate check, * else `false`. */ function baseSome(collection, predicate) { var result; baseEach(collection, function(value, index, collection) { result = predicate(value, index, collection); return !result; }); return !!result; } /** * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which * performs a binary search of `array` to determine the index at which `value` * should be inserted into `array` in order to maintain its sort order. * * @private * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @param {boolean} [retHighest] Specify returning the highest qualified index. * @returns {number} Returns the index at which `value` should be inserted * into `array`. */ function baseSortedIndex(array, value, retHighest) { var low = 0, high = array == null ? low : array.length; if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { while (low < high) { var mid = (low + high) >>> 1, computed = array[mid]; if (computed !== null && !isSymbol(computed) && (retHighest ? (computed <= value) : (computed < value))) { low = mid + 1; } else { high = mid; } } return high; } return baseSortedIndexBy(array, value, identity, retHighest); } /** * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy` * which invokes `iteratee` for `value` and each element of `array` to compute * their sort ranking. The iteratee is invoked with one argument; (value). * * @private * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @param {Function} iteratee The iteratee invoked per element. * @param {boolean} [retHighest] Specify returning the highest qualified index. * @returns {number} Returns the index at which `value` should be inserted * into `array`. */ function baseSortedIndexBy(array, value, iteratee, retHighest) { value = iteratee(value); var low = 0, high = array == null ? 0 : array.length, valIsNaN = value !== value, valIsNull = value === null, valIsSymbol = isSymbol(value), valIsUndefined = value === undefined; while (low < high) { var mid = nativeFloor((low + high) / 2), computed = iteratee(array[mid]), othIsDefined = computed !== undefined, othIsNull = computed === null, othIsReflexive = computed === computed, othIsSymbol = isSymbol(computed); if (valIsNaN) { var setLow = retHighest || othIsReflexive; } else if (valIsUndefined) { setLow = othIsReflexive && (retHighest || othIsDefined); } else if (valIsNull) { setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull); } else if (valIsSymbol) { setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol); } else if (othIsNull || othIsSymbol) { setLow = false; } else { setLow = retHighest ? (computed <= value) : (computed < value); } if (setLow) { low = mid + 1; } else { high = mid; } } return nativeMin(high, MAX_ARRAY_INDEX); } /** * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without * support for iteratee shorthands. * * @private * @param {Array} array The array to inspect. * @param {Function} [iteratee] The iteratee invoked per element. * @returns {Array} Returns the new duplicate free array. */ function baseSortedUniq(array, iteratee) { var index = -1, length = array.length, resIndex = 0, result = []; while (++index < length) { var value = array[index], computed = iteratee ? iteratee(value) : value; if (!index || !eq(computed, seen)) { var seen = computed; result[resIndex++] = value === 0 ? 0 : value; } } return result; } /** * The base implementation of `_.toNumber` which doesn't ensure correct * conversions of binary, hexadecimal, or octal string values. * * @private * @param {*} value The value to process. * @returns {number} Returns the number. */ function baseToNumber(value) { if (typeof value == 'number') { return value; } if (isSymbol(value)) { return NAN; } return +value; } /** * The base implementation of `_.toString` which doesn't convert nullish * values to empty strings. * * @private * @param {*} value The value to process. * @returns {string} Returns the string. */ function baseToString(value) { // Exit early for strings to avoid a performance hit in some environments. if (typeof value == 'string') { return value; } if (isArray(value)) { // Recursively convert values (susceptible to call stack limits). return arrayMap(value, baseToString) + ''; } if (isSymbol(value)) { return symbolToString ? symbolToString.call(value) : ''; } var result = (value + ''); return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; } /** * The base implementation of `_.uniqBy` without support for iteratee shorthands. * * @private * @param {Array} array The array to inspect. * @param {Function} [iteratee] The iteratee invoked per element. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new duplicate free array. */ function baseUniq(array, iteratee, comparator) { var index = -1, includes = arrayIncludes, length = array.length, isCommon = true, result = [], seen = result; if (comparator) { isCommon = false; includes = arrayIncludesWith; } else if (length >= LARGE_ARRAY_SIZE) { var set = iteratee ? null : createSet(array); if (set) { return setToArray(set); } isCommon = false; includes = cacheHas; seen = new SetCache; } else { seen = iteratee ? [] : result; } outer: while (++index < length) { var value = array[index], computed = iteratee ? iteratee(value) : value; value = (comparator || value !== 0) ? value : 0; if (isCommon && computed === computed) { var seenIndex = seen.length; while (seenIndex--) { if (seen[seenIndex] === computed) { continue outer; } } if (iteratee) { seen.push(computed); } result.push(value); } else if (!includes(seen, computed, comparator)) { if (seen !== result) { seen.push(computed); } result.push(value); } } return result; } /** * The base implementation of `_.unset`. * * @private * @param {Object} object The object to modify. * @param {Array|string} path The property path to unset. * @returns {boolean} Returns `true` if the property is deleted, else `false`. */ function baseUnset(object, path) { path = castPath(path, object); object = parent(object, path); return object == null || delete object[toKey(last(path))]; } /** * The base implementation of `_.update`. * * @private * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to update. * @param {Function} updater The function to produce the updated value. * @param {Function} [customizer] The function to customize path creation. * @returns {Object} Returns `object`. */ function baseUpdate(object, path, updater, customizer) { return baseSet(object, path, updater(baseGet(object, path)), customizer); } /** * The base implementation of methods like `_.dropWhile` and `_.takeWhile` * without support for iteratee shorthands. * * @private * @param {Array} array The array to query. * @param {Function} predicate The function invoked per iteration. * @param {boolean} [isDrop] Specify dropping elements instead of taking them. * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Array} Returns the slice of `array`. */ function baseWhile(array, predicate, isDrop, fromRight) { var length = array.length, index = fromRight ? length : -1; while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {} return isDrop ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length)) : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index)); } /** * The base implementation of `wrapperValue` which returns the result of * performing a sequence of actions on the unwrapped `value`, where each * successive action is supplied the return value of the previous. * * @private * @param {*} value The unwrapped value. * @param {Array} actions Actions to perform to resolve the unwrapped value. * @returns {*} Returns the resolved value. */ function baseWrapperValue(value, actions) { var result = value; if (result instanceof LazyWrapper) { result = result.value(); } return arrayReduce(actions, function(result, action) { return action.func.apply(action.thisArg, arrayPush([result], action.args)); }, result); } /** * The base implementation of methods like `_.xor`, without support for * iteratee shorthands, that accepts an array of arrays to inspect. * * @private * @param {Array} arrays The arrays to inspect. * @param {Function} [iteratee] The iteratee invoked per element. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of values. */ function baseXor(arrays, iteratee, comparator) { var length = arrays.length; if (length < 2) { return length ? baseUniq(arrays[0]) : []; } var index = -1, result = Array(length); while (++index < length) { var array = arrays[index], othIndex = -1; while (++othIndex < length) { if (othIndex != index) { result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator); } } } return baseUniq(baseFlatten(result, 1), iteratee, comparator); } /** * This base implementation of `_.zipObject` which assigns values using `assignFunc`. * * @private * @param {Array} props The property identifiers. * @param {Array} values The property values. * @param {Function} assignFunc The function to assign values. * @returns {Object} Returns the new object. */ function baseZipObject(props, values, assignFunc) { var index = -1, length = props.length, valsLength = values.length, result = {}; while (++index < length) { var value = index < valsLength ? values[index] : undefined; assignFunc(result, props[index], value); } return result; } /** * Casts `value` to an empty array if it's not an array like object. * * @private * @param {*} value The value to inspect. * @returns {Array|Object} Returns the cast array-like object. */ function castArrayLikeObject(value) { return isArrayLikeObject(value) ? value : []; } /** * Casts `value` to `identity` if it's not a function. * * @private * @param {*} value The value to inspect. * @returns {Function} Returns cast function. */ function castFunction(value) { return typeof value == 'function' ? value : identity; } /** * Casts `value` to a path array if it's not one. * * @private * @param {*} value The value to inspect. * @param {Object} [object] The object to query keys on. * @returns {Array} Returns the cast property path array. */ function castPath(value, object) { if (isArray(value)) { return value; } return isKey(value, object) ? [value] : stringToPath(toString(value)); } /** * A `baseRest` alias which can be replaced with `identity` by module * replacement plugins. * * @private * @type {Function} * @param {Function} func The function to apply a rest parameter to. * @returns {Function} Returns the new function. */ var castRest = baseRest; /** * Casts `array` to a slice if it's needed. * * @private * @param {Array} array The array to inspect. * @param {number} start The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the cast slice. */ function castSlice(array, start, end) { var length = array.length; end = end === undefined ? length : end; return (!start && end >= length) ? array : baseSlice(array, start, end); } /** * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout). * * @private * @param {number|Object} id The timer id or timeout object of the timer to clear. */ var clearTimeout = ctxClearTimeout || function(id) { return root.clearTimeout(id); }; /** * Creates a clone of `buffer`. * * @private * @param {Buffer} buffer The buffer to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Buffer} Returns the cloned buffer. */ function cloneBuffer(buffer, isDeep) { if (isDeep) { return buffer.slice(); } var length = buffer.length, result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); buffer.copy(result); return result; } /** * Creates a clone of `arrayBuffer`. * * @private * @param {ArrayBuffer} arrayBuffer The array buffer to clone. * @returns {ArrayBuffer} Returns the cloned array buffer. */ function cloneArrayBuffer(arrayBuffer) { var result = new arrayBuffer.constructor(arrayBuffer.byteLength); new Uint8Array(result).set(new Uint8Array(arrayBuffer)); return result; } /** * Creates a clone of `dataView`. * * @private * @param {Object} dataView The data view to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the cloned data view. */ function cloneDataView(dataView, isDeep) { var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); } /** * Creates a clone of `regexp`. * * @private * @param {Object} regexp The regexp to clone. * @returns {Object} Returns the cloned regexp. */ function cloneRegExp(regexp) { var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); result.lastIndex = regexp.lastIndex; return result; } /** * Creates a clone of the `symbol` object. * * @private * @param {Object} symbol The symbol object to clone. * @returns {Object} Returns the cloned symbol object. */ function cloneSymbol(symbol) { return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; } /** * Creates a clone of `typedArray`. * * @private * @param {Object} typedArray The typed array to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the cloned typed array. */ function cloneTypedArray(typedArray, isDeep) { var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); } /** * Compares values to sort them in ascending order. * * @private * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {number} Returns the sort order indicator for `value`. */ function compareAscending(value, other) { if (value !== other) { var valIsDefined = value !== undefined, valIsNull = value === null, valIsReflexive = value === value, valIsSymbol = isSymbol(value); var othIsDefined = other !== undefined, othIsNull = other === null, othIsReflexive = other === other, othIsSymbol = isSymbol(other); if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) || (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) || (valIsNull && othIsDefined && othIsReflexive) || (!valIsDefined && othIsReflexive) || !valIsReflexive) { return 1; } if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) || (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) || (othIsNull && valIsDefined && valIsReflexive) || (!othIsDefined && valIsReflexive) || !othIsReflexive) { return -1; } } return 0; } /** * Used by `_.orderBy` to compare multiple properties of a value to another * and stable sort them. * * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, * specify an order of "desc" for descending or "asc" for ascending sort order * of corresponding values. * * @private * @param {Object} object The object to compare. * @param {Object} other The other object to compare. * @param {boolean[]|string[]} orders The order to sort by for each property. * @returns {number} Returns the sort order indicator for `object`. */ function compareMultiple(object, other, orders) { var index = -1, objCriteria = object.criteria, othCriteria = other.criteria, length = objCriteria.length, ordersLength = orders.length; while (++index < length) { var result = compareAscending(objCriteria[index], othCriteria[index]); if (result) { if (index >= ordersLength) { return result; } var order = orders[index]; return result * (order == 'desc' ? -1 : 1); } } // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications // that causes it, under certain circumstances, to provide the same value for // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 // for more details. // // This also ensures a stable sort in V8 and other engines. // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. return object.index - other.index; } /** * Creates an array that is the composition of partially applied arguments, * placeholders, and provided arguments into a single array of arguments. * * @private * @param {Array} args The provided arguments. * @param {Array} partials The arguments to prepend to those provided. * @param {Array} holders The `partials` placeholder indexes. * @params {boolean} [isCurried] Specify composing for a curried function. * @returns {Array} Returns the new array of composed arguments. */ function composeArgs(args, partials, holders, isCurried) { var argsIndex = -1, argsLength = args.length, holdersLength = holders.length, leftIndex = -1, leftLength = partials.length, rangeLength = nativeMax(argsLength - holdersLength, 0), result = Array(leftLength + rangeLength), isUncurried = !isCurried; while (++leftIndex < leftLength) { result[leftIndex] = partials[leftIndex]; } while (++argsIndex < holdersLength) { if (isUncurried || argsIndex < argsLength) { result[holders[argsIndex]] = args[argsIndex]; } } while (rangeLength--) { result[leftIndex++] = args[argsIndex++]; } return result; } /** * This function is like `composeArgs` except that the arguments composition * is tailored for `_.partialRight`. * * @private * @param {Array} args The provided arguments. * @param {Array} partials The arguments to append to those provided. * @param {Array} holders The `partials` placeholder indexes. * @params {boolean} [isCurried] Specify composing for a curried function. * @returns {Array} Returns the new array of composed arguments. */ function composeArgsRight(args, partials, holders, isCurried) { var argsIndex = -1, argsLength = args.length, holdersIndex = -1, holdersLength = holders.length, rightIndex = -1, rightLength = partials.length, rangeLength = nativeMax(argsLength - holdersLength, 0), result = Array(rangeLength + rightLength), isUncurried = !isCurried; while (++argsIndex < rangeLength) { result[argsIndex] = args[argsIndex]; } var offset = argsIndex; while (++rightIndex < rightLength) { result[offset + rightIndex] = partials[rightIndex]; } while (++holdersIndex < holdersLength) { if (isUncurried || argsIndex < argsLength) { result[offset + holders[holdersIndex]] = args[argsIndex++]; } } return result; } /** * Copies the values of `source` to `array`. * * @private * @param {Array} source The array to copy values from. * @param {Array} [array=[]] The array to copy values to. * @returns {Array} Returns `array`. */ function copyArray(source, array) { var index = -1, length = source.length; array || (array = Array(length)); while (++index < length) { array[index] = source[index]; } return array; } /** * Copies properties of `source` to `object`. * * @private * @param {Object} source The object to copy properties from. * @param {Array} props The property identifiers to copy. * @param {Object} [object={}] The object to copy properties to. * @param {Function} [customizer] The function to customize copied values. * @returns {Object} Returns `object`. */ function copyObject(source, props, object, customizer) { var isNew = !object; object || (object = {}); var index = -1, length = props.length; while (++index < length) { var key = props[index]; var newValue = customizer ? customizer(object[key], source[key], key, object, source) : undefined; if (newValue === undefined) { newValue = source[key]; } if (isNew) { baseAssignValue(object, key, newValue); } else { assignValue(object, key, newValue); } } return object; } /** * Copies own symbols of `source` to `object`. * * @private * @param {Object} source The object to copy symbols from. * @param {Object} [object={}] The object to copy symbols to. * @returns {Object} Returns `object`. */ function copySymbols(source, object) { return copyObject(source, getSymbols(source), object); } /** * Copies own and inherited symbols of `source` to `object`. * * @private * @param {Object} source The object to copy symbols from. * @param {Object} [object={}] The object to copy symbols to. * @returns {Object} Returns `object`. */ function copySymbolsIn(source, object) { return copyObject(source, getSymbolsIn(source), object); } /** * Creates a function like `_.groupBy`. * * @private * @param {Function} setter The function to set accumulator values. * @param {Function} [initializer] The accumulator object initializer. * @returns {Function} Returns the new aggregator function. */ function createAggregator(setter, initializer) { return function(collection, iteratee) { var func = isArray(collection) ? arrayAggregator : baseAggregator, accumulator = initializer ? initializer() : {}; return func(collection, setter, getIteratee(iteratee, 2), accumulator); }; } /** * Creates a function like `_.assign`. * * @private * @param {Function} assigner The function to assign values. * @returns {Function} Returns the new assigner function. */ function createAssigner(assigner) { return baseRest(function(object, sources) { var index = -1, length = sources.length, customizer = length > 1 ? sources[length - 1] : undefined, guard = length > 2 ? sources[2] : undefined; customizer = (assigner.length > 3 && typeof customizer == 'function') ? (length--, customizer) : undefined; if (guard && isIterateeCall(sources[0], sources[1], guard)) { customizer = length < 3 ? undefined : customizer; length = 1; } object = Object(object); while (++index < length) { var source = sources[index]; if (source) { assigner(object, source, index, customizer); } } return object; }); } /** * Creates a `baseEach` or `baseEachRight` function. * * @private * @param {Function} eachFunc The function to iterate over a collection. * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Function} Returns the new base function. */ function createBaseEach(eachFunc, fromRight) { return function(collection, iteratee) { if (collection == null) { return collection; } if (!isArrayLike(collection)) { return eachFunc(collection, iteratee); } var length = collection.length, index = fromRight ? length : -1, iterable = Object(collection); while ((fromRight ? index-- : ++index < length)) { if (iteratee(iterable[index], index, iterable) === false) { break; } } return collection; }; } /** * Creates a base function for methods like `_.forIn` and `_.forOwn`. * * @private * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Function} Returns the new base function. */ function createBaseFor(fromRight) { return function(object, iteratee, keysFunc) { var index = -1, iterable = Object(object), props = keysFunc(object), length = props.length; while (length--) { var key = props[fromRight ? length : ++index]; if (iteratee(iterable[key], key, iterable) === false) { break; } } return object; }; } /** * Creates a function that wraps `func` to invoke it with the optional `this` * binding of `thisArg`. * * @private * @param {Function} func The function to wrap. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @param {*} [thisArg] The `this` binding of `func`. * @returns {Function} Returns the new wrapped function. */ function createBind(func, bitmask, thisArg) { var isBind = bitmask & WRAP_BIND_FLAG, Ctor = createCtor(func); function wrapper() { var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; return fn.apply(isBind ? thisArg : this, arguments); } return wrapper; } /** * Creates a function like `_.lowerFirst`. * * @private * @param {string} methodName The name of the `String` case method to use. * @returns {Function} Returns the new case function. */ function createCaseFirst(methodName) { return function(string) { string = toString(string); var strSymbols = hasUnicode(string) ? stringToArray(string) : undefined; var chr = strSymbols ? strSymbols[0] : string.charAt(0); var trailing = strSymbols ? castSlice(strSymbols, 1).join('') : string.slice(1); return chr[methodName]() + trailing; }; } /** * Creates a function like `_.camelCase`. * * @private * @param {Function} callback The function to combine each word. * @returns {Function} Returns the new compounder function. */ function createCompounder(callback) { return function(string) { return arrayReduce(words(deburr(string).replace(reApos, '')), callback, ''); }; } /** * Creates a function that produces an instance of `Ctor` regardless of * whether it was invoked as part of a `new` expression or by `call` or `apply`. * * @private * @param {Function} Ctor The constructor to wrap. * @returns {Function} Returns the new wrapped function. */ function createCtor(Ctor) { return function() { // Use a `switch` statement to work with class constructors. See // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist // for more details. var args = arguments; switch (args.length) { case 0: return new Ctor; case 1: return new Ctor(args[0]); case 2: return new Ctor(args[0], args[1]); case 3: return new Ctor(args[0], args[1], args[2]); case 4: return new Ctor(args[0], args[1], args[2], args[3]); case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]); case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]); case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); } var thisBinding = baseCreate(Ctor.prototype), result = Ctor.apply(thisBinding, args); // Mimic the constructor's `return` behavior. // See https://es5.github.io/#x13.2.2 for more details. return isObject(result) ? result : thisBinding; }; } /** * Creates a function that wraps `func` to enable currying. * * @private * @param {Function} func The function to wrap. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @param {number} arity The arity of `func`. * @returns {Function} Returns the new wrapped function. */ function createCurry(func, bitmask, arity) { var Ctor = createCtor(func); function wrapper() { var length = arguments.length, args = Array(length), index = length, placeholder = getHolder(wrapper); while (index--) { args[index] = arguments[index]; } var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder) ? [] : replaceHolders(args, placeholder); length -= holders.length; if (length < arity) { return createRecurry( func, bitmask, createHybrid, wrapper.placeholder, undefined, args, holders, undefined, undefined, arity - length); } var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; return apply(fn, this, args); } return wrapper; } /** * Creates a `_.find` or `_.findLast` function. * * @private * @param {Function} findIndexFunc The function to find the collection index. * @returns {Function} Returns the new find function. */ function createFind(findIndexFunc) { return function(collection, predicate, fromIndex) { var iterable = Object(collection); if (!isArrayLike(collection)) { var iteratee = getIteratee(predicate, 3); collection = keys(collection); predicate = function(key) { return iteratee(iterable[key], key, iterable); }; } var index = findIndexFunc(collection, predicate, fromIndex); return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined; }; } /** * Creates a `_.flow` or `_.flowRight` function. * * @private * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Function} Returns the new flow function. */ function createFlow(fromRight) { return flatRest(function(funcs) { var length = funcs.length, index = length, prereq = LodashWrapper.prototype.thru; if (fromRight) { funcs.reverse(); } while (index--) { var func = funcs[index]; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } if (prereq && !wrapper && getFuncName(func) == 'wrapper') { var wrapper = new LodashWrapper([], true); } } index = wrapper ? index : length; while (++index < length) { func = funcs[index]; var funcName = getFuncName(func), data = funcName == 'wrapper' ? getData(func) : undefined; if (data && isLaziable(data[0]) && data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) && !data[4].length && data[9] == 1 ) { wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]); } else { wrapper = (func.length == 1 && isLaziable(func)) ? wrapper[funcName]() : wrapper.thru(func); } } return function() { var args = arguments, value = args[0]; if (wrapper && args.length == 1 && isArray(value)) { return wrapper.plant(value).value(); } var index = 0, result = length ? funcs[index].apply(this, args) : value; while (++index < length) { result = funcs[index].call(this, result); } return result; }; }); } /** * Creates a function that wraps `func` to invoke it with optional `this` * binding of `thisArg`, partial application, and currying. * * @private * @param {Function|string} func The function or method name to wrap. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @param {*} [thisArg] The `this` binding of `func`. * @param {Array} [partials] The arguments to prepend to those provided to * the new function. * @param {Array} [holders] The `partials` placeholder indexes. * @param {Array} [partialsRight] The arguments to append to those provided * to the new function. * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. * @param {Array} [argPos] The argument positions of the new function. * @param {number} [ary] The arity cap of `func`. * @param {number} [arity] The arity of `func`. * @returns {Function} Returns the new wrapped function. */ function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { var isAry = bitmask & WRAP_ARY_FLAG, isBind = bitmask & WRAP_BIND_FLAG, isBindKey = bitmask & WRAP_BIND_KEY_FLAG, isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG), isFlip = bitmask & WRAP_FLIP_FLAG, Ctor = isBindKey ? undefined : createCtor(func); function wrapper() { var length = arguments.length, args = Array(length), index = length; while (index--) { args[index] = arguments[index]; } if (isCurried) { var placeholder = getHolder(wrapper), holdersCount = countHolders(args, placeholder); } if (partials) { args = composeArgs(args, partials, holders, isCurried); } if (partialsRight) { args = composeArgsRight(args, partialsRight, holdersRight, isCurried); } length -= holdersCount; if (isCurried && length < arity) { var newHolders = replaceHolders(args, placeholder); return createRecurry( func, bitmask, createHybrid, wrapper.placeholder, thisArg, args, newHolders, argPos, ary, arity - length ); } var thisBinding = isBind ? thisArg : this, fn = isBindKey ? thisBinding[func] : func; length = args.length; if (argPos) { args = reorder(args, argPos); } else if (isFlip && length > 1) { args.reverse(); } if (isAry && ary < length) { args.length = ary; } if (this && this !== root && this instanceof wrapper) { fn = Ctor || createCtor(fn); } return fn.apply(thisBinding, args); } return wrapper; } /** * Creates a function like `_.invertBy`. * * @private * @param {Function} setter The function to set accumulator values. * @param {Function} toIteratee The function to resolve iteratees. * @returns {Function} Returns the new inverter function. */ function createInverter(setter, toIteratee) { return function(object, iteratee) { return baseInverter(object, setter, toIteratee(iteratee), {}); }; } /** * Creates a function that performs a mathematical operation on two values. * * @private * @param {Function} operator The function to perform the operation. * @param {number} [defaultValue] The value used for `undefined` arguments. * @returns {Function} Returns the new mathematical operation function. */ function createMathOperation(operator, defaultValue) { return function(value, other) { var result; if (value === undefined && other === undefined) { return defaultValue; } if (value !== undefined) { result = value; } if (other !== undefined) { if (result === undefined) { return other; } if (typeof value == 'string' || typeof other == 'string') { value = baseToString(value); other = baseToString(other); } else { value = baseToNumber(value); other = baseToNumber(other); } result = operator(value, other); } return result; }; } /** * Creates a function like `_.over`. * * @private * @param {Function} arrayFunc The function to iterate over iteratees. * @returns {Function} Returns the new over function. */ function createOver(arrayFunc) { return flatRest(function(iteratees) { iteratees = arrayMap(iteratees, baseUnary(getIteratee())); return baseRest(function(args) { var thisArg = this; return arrayFunc(iteratees, function(iteratee) { return apply(iteratee, thisArg, args); }); }); }); } /** * Creates the padding for `string` based on `length`. The `chars` string * is truncated if the number of characters exceeds `length`. * * @private * @param {number} length The padding length. * @param {string} [chars=' '] The string used as padding. * @returns {string} Returns the padding for `string`. */ function createPadding(length, chars) { chars = chars === undefined ? ' ' : baseToString(chars); var charsLength = chars.length; if (charsLength < 2) { return charsLength ? baseRepeat(chars, length) : chars; } var result = baseRepeat(chars, nativeCeil(length / stringSize(chars))); return hasUnicode(chars) ? castSlice(stringToArray(result), 0, length).join('') : result.slice(0, length); } /** * Creates a function that wraps `func` to invoke it with the `this` binding * of `thisArg` and `partials` prepended to the arguments it receives. * * @private * @param {Function} func The function to wrap. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @param {*} thisArg The `this` binding of `func`. * @param {Array} partials The arguments to prepend to those provided to * the new function. * @returns {Function} Returns the new wrapped function. */ function createPartial(func, bitmask, thisArg, partials) { var isBind = bitmask & WRAP_BIND_FLAG, Ctor = createCtor(func); function wrapper() { var argsIndex = -1, argsLength = arguments.length, leftIndex = -1, leftLength = partials.length, args = Array(leftLength + argsLength), fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; while (++leftIndex < leftLength) { args[leftIndex] = partials[leftIndex]; } while (argsLength--) { args[leftIndex++] = arguments[++argsIndex]; } return apply(fn, isBind ? thisArg : this, args); } return wrapper; } /** * Creates a `_.range` or `_.rangeRight` function. * * @private * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Function} Returns the new range function. */ function createRange(fromRight) { return function(start, end, step) { if (step && typeof step != 'number' && isIterateeCall(start, end, step)) { end = step = undefined; } // Ensure the sign of `-0` is preserved. start = toFinite(start); if (end === undefined) { end = start; start = 0; } else { end = toFinite(end); } step = step === undefined ? (start < end ? 1 : -1) : toFinite(step); return baseRange(start, end, step, fromRight); }; } /** * Creates a function that performs a relational operation on two values. * * @private * @param {Function} operator The function to perform the operation. * @returns {Function} Returns the new relational operation function. */ function createRelationalOperation(operator) { return function(value, other) { if (!(typeof value == 'string' && typeof other == 'string')) { value = toNumber(value); other = toNumber(other); } return operator(value, other); }; } /** * Creates a function that wraps `func` to continue currying. * * @private * @param {Function} func The function to wrap. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @param {Function} wrapFunc The function to create the `func` wrapper. * @param {*} placeholder The placeholder value. * @param {*} [thisArg] The `this` binding of `func`. * @param {Array} [partials] The arguments to prepend to those provided to * the new function. * @param {Array} [holders] The `partials` placeholder indexes. * @param {Array} [argPos] The argument positions of the new function. * @param {number} [ary] The arity cap of `func`. * @param {number} [arity] The arity of `func`. * @returns {Function} Returns the new wrapped function. */ function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) { var isCurry = bitmask & WRAP_CURRY_FLAG, newHolders = isCurry ? holders : undefined, newHoldersRight = isCurry ? undefined : holders, newPartials = isCurry ? partials : undefined, newPartialsRight = isCurry ? undefined : partials; bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG); bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG); if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) { bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG); } var newData = [ func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, newHoldersRight, argPos, ary, arity ]; var result = wrapFunc.apply(undefined, newData); if (isLaziable(func)) { setData(result, newData); } result.placeholder = placeholder; return setWrapToString(result, func, bitmask); } /** * Creates a function like `_.round`. * * @private * @param {string} methodName The name of the `Math` method to use when rounding. * @returns {Function} Returns the new round function. */ function createRound(methodName) { var func = Math[methodName]; return function(number, precision) { number = toNumber(number); precision = precision == null ? 0 : nativeMin(toInteger(precision), 292); if (precision) { // Shift with exponential notation to avoid floating-point issues. // See [MDN](https://mdn.io/round#Examples) for more details. var pair = (toString(number) + 'e').split('e'), value = func(pair[0] + 'e' + (+pair[1] + precision)); pair = (toString(value) + 'e').split('e'); return +(pair[0] + 'e' + (+pair[1] - precision)); } return func(number); }; } /** * Creates a set object of `values`. * * @private * @param {Array} values The values to add to the set. * @returns {Object} Returns the new set. */ var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) { return new Set(values); }; /** * Creates a `_.toPairs` or `_.toPairsIn` function. * * @private * @param {Function} keysFunc The function to get the keys of a given object. * @returns {Function} Returns the new pairs function. */ function createToPairs(keysFunc) { return function(object) { var tag = getTag(object); if (tag == mapTag) { return mapToArray(object); } if (tag == setTag) { return setToPairs(object); } return baseToPairs(object, keysFunc(object)); }; } /** * Creates a function that either curries or invokes `func` with optional * `this` binding and partially applied arguments. * * @private * @param {Function|string} func The function or method name to wrap. * @param {number} bitmask The bitmask flags. * 1 - `_.bind` * 2 - `_.bindKey` * 4 - `_.curry` or `_.curryRight` of a bound function * 8 - `_.curry` * 16 - `_.curryRight` * 32 - `_.partial` * 64 - `_.partialRight` * 128 - `_.rearg` * 256 - `_.ary` * 512 - `_.flip` * @param {*} [thisArg] The `this` binding of `func`. * @param {Array} [partials] The arguments to be partially applied. * @param {Array} [holders] The `partials` placeholder indexes. * @param {Array} [argPos] The argument positions of the new function. * @param {number} [ary] The arity cap of `func`. * @param {number} [arity] The arity of `func`. * @returns {Function} Returns the new wrapped function. */ function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { var isBindKey = bitmask & WRAP_BIND_KEY_FLAG; if (!isBindKey && typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } var length = partials ? partials.length : 0; if (!length) { bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG); partials = holders = undefined; } ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0); arity = arity === undefined ? arity : toInteger(arity); length -= holders ? holders.length : 0; if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) { var partialsRight = partials, holdersRight = holders; partials = holders = undefined; } var data = isBindKey ? undefined : getData(func); var newData = [ func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity ]; if (data) { mergeData(newData, data); } func = newData[0]; bitmask = newData[1]; thisArg = newData[2]; partials = newData[3]; holders = newData[4]; arity = newData[9] = newData[9] === undefined ? (isBindKey ? 0 : func.length) : nativeMax(newData[9] - length, 0); if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) { bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG); } if (!bitmask || bitmask == WRAP_BIND_FLAG) { var result = createBind(func, bitmask, thisArg); } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) { result = createCurry(func, bitmask, arity); } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) { result = createPartial(func, bitmask, thisArg, partials); } else { result = createHybrid.apply(undefined, newData); } var setter = data ? baseSetData : setData; return setWrapToString(setter(result, newData), func, bitmask); } /** * Used by `_.defaults` to customize its `_.assignIn` use to assign properties * of source objects to the destination object for all destination properties * that resolve to `undefined`. * * @private * @param {*} objValue The destination value. * @param {*} srcValue The source value. * @param {string} key The key of the property to assign. * @param {Object} object The parent object of `objValue`. * @returns {*} Returns the value to assign. */ function customDefaultsAssignIn(objValue, srcValue, key, object) { if (objValue === undefined || (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) { return srcValue; } return objValue; } /** * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source * objects into destination objects that are passed thru. * * @private * @param {*} objValue The destination value. * @param {*} srcValue The source value. * @param {string} key The key of the property to merge. * @param {Object} object The parent object of `objValue`. * @param {Object} source The parent object of `srcValue`. * @param {Object} [stack] Tracks traversed source values and their merged * counterparts. * @returns {*} Returns the value to assign. */ function customDefaultsMerge(objValue, srcValue, key, object, source, stack) { if (isObject(objValue) && isObject(srcValue)) { // Recursively merge objects and arrays (susceptible to call stack limits). stack.set(srcValue, objValue); baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack); stack['delete'](srcValue); } return objValue; } /** * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain * objects. * * @private * @param {*} value The value to inspect. * @param {string} key The key of the property to inspect. * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`. */ function customOmitClone(value) { return isPlainObject(value) ? undefined : value; } /** * A specialized version of `baseIsEqualDeep` for arrays with support for * partial deep comparisons. * * @private * @param {Array} array The array to compare. * @param {Array} other The other array to compare. * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. * @param {Function} customizer The function to customize comparisons. * @param {Function} equalFunc The function to determine equivalents of values. * @param {Object} stack Tracks traversed `array` and `other` objects. * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. */ function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { var isPartial = bitmask & COMPARE_PARTIAL_FLAG, arrLength = array.length, othLength = other.length; if (arrLength != othLength && !(isPartial && othLength > arrLength)) { return false; } // Assume cyclic values are equal. var stacked = stack.get(array); if (stacked && stack.get(other)) { return stacked == other; } var index = -1, result = true, seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined; stack.set(array, other); stack.set(other, array); // Ignore non-index properties. while (++index < arrLength) { var arrValue = array[index], othValue = other[index]; if (customizer) { var compared = isPartial ? customizer(othValue, arrValue, index, other, array, stack) : customizer(arrValue, othValue, index, array, other, stack); } if (compared !== undefined) { if (compared) { continue; } result = false; break; } // Recursively compare arrays (susceptible to call stack limits). if (seen) { if (!arraySome(other, function(othValue, othIndex) { if (!cacheHas(seen, othIndex) && (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { return seen.push(othIndex); } })) { result = false; break; } } else if (!( arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack) )) { result = false; break; } } stack['delete'](array); stack['delete'](other); return result; } /** * A specialized version of `baseIsEqualDeep` for comparing objects of * the same `toStringTag`. * * **Note:** This function only supports comparing values with tags of * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. * * @private * @param {Object} object The object to compare. * @param {Object} other The other object to compare. * @param {string} tag The `toStringTag` of the objects to compare. * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. * @param {Function} customizer The function to customize comparisons. * @param {Function} equalFunc The function to determine equivalents of values. * @param {Object} stack Tracks traversed `object` and `other` objects. * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. */ function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { switch (tag) { case dataViewTag: if ((object.byteLength != other.byteLength) || (object.byteOffset != other.byteOffset)) { return false; } object = object.buffer; other = other.buffer; case arrayBufferTag: if ((object.byteLength != other.byteLength) || !equalFunc(new Uint8Array(object), new Uint8Array(other))) { return false; } return true; case boolTag: case dateTag: case numberTag: // Coerce booleans to `1` or `0` and dates to milliseconds. // Invalid dates are coerced to `NaN`. return eq(+object, +other); case errorTag: return object.name == other.name && object.message == other.message; case regexpTag: case stringTag: // Coerce regexes to strings and treat strings, primitives and objects, // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring // for more details. return object == (other + ''); case mapTag: var convert = mapToArray; case setTag: var isPartial = bitmask & COMPARE_PARTIAL_FLAG; convert || (convert = setToArray); if (object.size != other.size && !isPartial) { return false; } // Assume cyclic values are equal. var stacked = stack.get(object); if (stacked) { return stacked == other; } bitmask |= COMPARE_UNORDERED_FLAG; // Recursively compare objects (susceptible to call stack limits). stack.set(object, other); var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack); stack['delete'](object); return result; case symbolTag: if (symbolValueOf) { return symbolValueOf.call(object) == symbolValueOf.call(other); } } return false; } /** * A specialized version of `baseIsEqualDeep` for objects with support for * partial deep comparisons. * * @private * @param {Object} object The object to compare. * @param {Object} other The other object to compare. * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. * @param {Function} customizer The function to customize comparisons. * @param {Function} equalFunc The function to determine equivalents of values. * @param {Object} stack Tracks traversed `object` and `other` objects. * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. */ function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { var isPartial = bitmask & COMPARE_PARTIAL_FLAG, objProps = getAllKeys(object), objLength = objProps.length, othProps = getAllKeys(other), othLength = othProps.length; if (objLength != othLength && !isPartial) { return false; } var index = objLength; while (index--) { var key = objProps[index]; if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { return false; } } // Assume cyclic values are equal. var stacked = stack.get(object); if (stacked && stack.get(other)) { return stacked == other; } var result = true; stack.set(object, other); stack.set(other, object); var skipCtor = isPartial; while (++index < objLength) { key = objProps[index]; var objValue = object[key], othValue = other[key]; if (customizer) { var compared = isPartial ? customizer(othValue, objValue, key, other, object, stack) : customizer(objValue, othValue, key, object, other, stack); } // Recursively compare objects (susceptible to call stack limits). if (!(compared === undefined ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack)) : compared )) { result = false; break; } skipCtor || (skipCtor = key == 'constructor'); } if (result && !skipCtor) { var objCtor = object.constructor, othCtor = other.constructor; // Non `Object` object instances with different constructors are not equal. if (objCtor != othCtor && ('constructor' in object && 'constructor' in other) && !(typeof objCtor == 'function' && objCtor instanceof objCtor && typeof othCtor == 'function' && othCtor instanceof othCtor)) { result = false; } } stack['delete'](object); stack['delete'](other); return result; } /** * A specialized version of `baseRest` which flattens the rest array. * * @private * @param {Function} func The function to apply a rest parameter to. * @returns {Function} Returns the new function. */ function flatRest(func) { return setToString(overRest(func, undefined, flatten), func + ''); } /** * Creates an array of own enumerable property names and symbols of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names and symbols. */ function getAllKeys(object) { return baseGetAllKeys(object, keys, getSymbols); } /** * Creates an array of own and inherited enumerable property names and * symbols of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names and symbols. */ function getAllKeysIn(object) { return baseGetAllKeys(object, keysIn, getSymbolsIn); } /** * Gets metadata for `func`. * * @private * @param {Function} func The function to query. * @returns {*} Returns the metadata for `func`. */ var getData = !metaMap ? noop : function(func) { return metaMap.get(func); }; /** * Gets the name of `func`. * * @private * @param {Function} func The function to query. * @returns {string} Returns the function name. */ function getFuncName(func) { var result = (func.name + ''), array = realNames[result], length = hasOwnProperty.call(realNames, result) ? array.length : 0; while (length--) { var data = array[length], otherFunc = data.func; if (otherFunc == null || otherFunc == func) { return data.name; } } return result; } /** * Gets the argument placeholder value for `func`. * * @private * @param {Function} func The function to inspect. * @returns {*} Returns the placeholder value. */ function getHolder(func) { var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func; return object.placeholder; } /** * Gets the appropriate "iteratee" function. If `_.iteratee` is customized, * this function returns the custom method, otherwise it returns `baseIteratee`. * If arguments are provided, the chosen function is invoked with them and * its result is returned. * * @private * @param {*} [value] The value to convert to an iteratee. * @param {number} [arity] The arity of the created iteratee. * @returns {Function} Returns the chosen function or its result. */ function getIteratee() { var result = lodash.iteratee || iteratee; result = result === iteratee ? baseIteratee : result; return arguments.length ? result(arguments[0], arguments[1]) : result; } /** * Gets the data for `map`. * * @private * @param {Object} map The map to query. * @param {string} key The reference key. * @returns {*} Returns the map data. */ function getMapData(map, key) { var data = map.__data__; return isKeyable(key) ? data[typeof key == 'string' ? 'string' : 'hash'] : data.map; } /** * Gets the property names, values, and compare flags of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the match data of `object`. */ function getMatchData(object) { var result = keys(object), length = result.length; while (length--) { var key = result[length], value = object[key]; result[length] = [key, value, isStrictComparable(value)]; } return result; } /** * Gets the native function at `key` of `object`. * * @private * @param {Object} object The object to query. * @param {string} key The key of the method to get. * @returns {*} Returns the function if it's native, else `undefined`. */ function getNative(object, key) { var value = getValue(object, key); return baseIsNative(value) ? value : undefined; } /** * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. * * @private * @param {*} value The value to query. * @returns {string} Returns the raw `toStringTag`. */ function getRawTag(value) { var isOwn = hasOwnProperty.call(value, symToStringTag), tag = value[symToStringTag]; try { value[symToStringTag] = undefined; var unmasked = true; } catch (e) {} var result = nativeObjectToString.call(value); if (unmasked) { if (isOwn) { value[symToStringTag] = tag; } else { delete value[symToStringTag]; } } return result; } /** * Creates an array of the own enumerable symbols of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of symbols. */ var getSymbols = !nativeGetSymbols ? stubArray : function(object) { if (object == null) { return []; } object = Object(object); return arrayFilter(nativeGetSymbols(object), function(symbol) { return propertyIsEnumerable.call(object, symbol); }); }; /** * Creates an array of the own and inherited enumerable symbols of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of symbols. */ var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) { var result = []; while (object) { arrayPush(result, getSymbols(object)); object = getPrototype(object); } return result; }; /** * Gets the `toStringTag` of `value`. * * @private * @param {*} value The value to query. * @returns {string} Returns the `toStringTag`. */ var getTag = baseGetTag; // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6. if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || (Map && getTag(new Map) != mapTag) || (Promise && getTag(Promise.resolve()) != promiseTag) || (Set && getTag(new Set) != setTag) || (WeakMap && getTag(new WeakMap) != weakMapTag)) { getTag = function(value) { var result = baseGetTag(value), Ctor = result == objectTag ? value.constructor : undefined, ctorString = Ctor ? toSource(Ctor) : ''; if (ctorString) { switch (ctorString) { case dataViewCtorString: return dataViewTag; case mapCtorString: return mapTag; case promiseCtorString: return promiseTag; case setCtorString: return setTag; case weakMapCtorString: return weakMapTag; } } return result; }; } /** * Gets the view, applying any `transforms` to the `start` and `end` positions. * * @private * @param {number} start The start of the view. * @param {number} end The end of the view. * @param {Array} transforms The transformations to apply to the view. * @returns {Object} Returns an object containing the `start` and `end` * positions of the view. */ function getView(start, end, transforms) { var index = -1, length = transforms.length; while (++index < length) { var data = transforms[index], size = data.size; switch (data.type) { case 'drop': start += size; break; case 'dropRight': end -= size; break; case 'take': end = nativeMin(end, start + size); break; case 'takeRight': start = nativeMax(start, end - size); break; } } return { 'start': start, 'end': end }; } /** * Extracts wrapper details from the `source` body comment. * * @private * @param {string} source The source to inspect. * @returns {Array} Returns the wrapper details. */ function getWrapDetails(source) { var match = source.match(reWrapDetails); return match ? match[1].split(reSplitDetails) : []; } /** * Checks if `path` exists on `object`. * * @private * @param {Object} object The object to query. * @param {Array|string} path The path to check. * @param {Function} hasFunc The function to check properties. * @returns {boolean} Returns `true` if `path` exists, else `false`. */ function hasPath(object, path, hasFunc) { path = castPath(path, object); var index = -1, length = path.length, result = false; while (++index < length) { var key = toKey(path[index]); if (!(result = object != null && hasFunc(object, key))) { break; } object = object[key]; } if (result || ++index != length) { return result; } length = object == null ? 0 : object.length; return !!length && isLength(length) && isIndex(key, length) && (isArray(object) || isArguments(object)); } /** * Initializes an array clone. * * @private * @param {Array} array The array to clone. * @returns {Array} Returns the initialized clone. */ function initCloneArray(array) { var length = array.length, result = new array.constructor(length); // Add properties assigned by `RegExp#exec`. if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { result.index = array.index; result.input = array.input; } return result; } /** * Initializes an object clone. * * @private * @param {Object} object The object to clone. * @returns {Object} Returns the initialized clone. */ function initCloneObject(object) { return (typeof object.constructor == 'function' && !isPrototype(object)) ? baseCreate(getPrototype(object)) : {}; } /** * Initializes an object clone based on its `toStringTag`. * * **Note:** This function only supports cloning values with tags of * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`. * * @private * @param {Object} object The object to clone. * @param {string} tag The `toStringTag` of the object to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the initialized clone. */ function initCloneByTag(object, tag, isDeep) { var Ctor = object.constructor; switch (tag) { case arrayBufferTag: return cloneArrayBuffer(object); case boolTag: case dateTag: return new Ctor(+object); case dataViewTag: return cloneDataView(object, isDeep); case float32Tag: case float64Tag: case int8Tag: case int16Tag: case int32Tag: case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: return cloneTypedArray(object, isDeep); case mapTag: return new Ctor; case numberTag: case stringTag: return new Ctor(object); case regexpTag: return cloneRegExp(object); case setTag: return new Ctor; case symbolTag: return cloneSymbol(object); } } /** * Inserts wrapper `details` in a comment at the top of the `source` body. * * @private * @param {string} source The source to modify. * @returns {Array} details The details to insert. * @returns {string} Returns the modified source. */ function insertWrapDetails(source, details) { var length = details.length; if (!length) { return source; } var lastIndex = length - 1; details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex]; details = details.join(length > 2 ? ', ' : ' '); return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n'); } /** * Checks if `value` is a flattenable `arguments` object or array. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is flattenable, else `false`. */ function isFlattenable(value) { return isArray(value) || isArguments(value) || !!(spreadableSymbol && value && value[spreadableSymbol]); } /** * Checks if `value` is a valid array-like index. * * @private * @param {*} value The value to check. * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. */ function isIndex(value, length) { var type = typeof value; length = length == null ? MAX_SAFE_INTEGER : length; return !!length && (type == 'number' || (type != 'symbol' && reIsUint.test(value))) && (value > -1 && value % 1 == 0 && value < length); } /** * Checks if the given arguments are from an iteratee call. * * @private * @param {*} value The potential iteratee value argument. * @param {*} index The potential iteratee index or key argument. * @param {*} object The potential iteratee object argument. * @returns {boolean} Returns `true` if the arguments are from an iteratee call, * else `false`. */ function isIterateeCall(value, index, object) { if (!isObject(object)) { return false; } var type = typeof index; if (type == 'number' ? (isArrayLike(object) && isIndex(index, object.length)) : (type == 'string' && index in object) ) { return eq(object[index], value); } return false; } /** * Checks if `value` is a property name and not a property path. * * @private * @param {*} value The value to check. * @param {Object} [object] The object to query keys on. * @returns {boolean} Returns `true` if `value` is a property name, else `false`. */ function isKey(value, object) { if (isArray(value)) { return false; } var type = typeof value; if (type == 'number' || type == 'symbol' || type == 'boolean' || value == null || isSymbol(value)) { return true; } return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || (object != null && value in Object(object)); } /** * Checks if `value` is suitable for use as unique object key. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is suitable, else `false`. */ function isKeyable(value) { var type = typeof value; return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') ? (value !== '__proto__') : (value === null); } /** * Checks if `func` has a lazy counterpart. * * @private * @param {Function} func The function to check. * @returns {boolean} Returns `true` if `func` has a lazy counterpart, * else `false`. */ function isLaziable(func) { var funcName = getFuncName(func), other = lodash[funcName]; if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) { return false; } if (func === other) { return true; } var data = getData(other); return !!data && func === data[0]; } /** * Checks if `func` has its source masked. * * @private * @param {Function} func The function to check. * @returns {boolean} Returns `true` if `func` is masked, else `false`. */ function isMasked(func) { return !!maskSrcKey && (maskSrcKey in func); } /** * Checks if `func` is capable of being masked. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `func` is maskable, else `false`. */ var isMaskable = coreJsData ? isFunction : stubFalse; /** * Checks if `value` is likely a prototype object. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. */ function isPrototype(value) { var Ctor = value && value.constructor, proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; return value === proto; } /** * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` if suitable for strict * equality comparisons, else `false`. */ function isStrictComparable(value) { return value === value && !isObject(value); } /** * A specialized version of `matchesProperty` for source values suitable * for strict equality comparisons, i.e. `===`. * * @private * @param {string} key The key of the property to get. * @param {*} srcValue The value to match. * @returns {Function} Returns the new spec function. */ function matchesStrictComparable(key, srcValue) { return function(object) { if (object == null) { return false; } return object[key] === srcValue && (srcValue !== undefined || (key in Object(object))); }; } /** * A specialized version of `_.memoize` which clears the memoized function's * cache when it exceeds `MAX_MEMOIZE_SIZE`. * * @private * @param {Function} func The function to have its output memoized. * @returns {Function} Returns the new memoized function. */ function memoizeCapped(func) { var result = memoize(func, function(key) { if (cache.size === MAX_MEMOIZE_SIZE) { cache.clear(); } return key; }); var cache = result.cache; return result; } /** * Merges the function metadata of `source` into `data`. * * Merging metadata reduces the number of wrappers used to invoke a function. * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` * may be applied regardless of execution order. Methods like `_.ary` and * `_.rearg` modify function arguments, making the order in which they are * executed important, preventing the merging of metadata. However, we make * an exception for a safe combined case where curried functions have `_.ary` * and or `_.rearg` applied. * * @private * @param {Array} data The destination metadata. * @param {Array} source The source metadata. * @returns {Array} Returns `data`. */ function mergeData(data, source) { var bitmask = data[1], srcBitmask = source[1], newBitmask = bitmask | srcBitmask, isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG); var isCombo = ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) || ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) || ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG)); // Exit early if metadata can't be merged. if (!(isCommon || isCombo)) { return data; } // Use source `thisArg` if available. if (srcBitmask & WRAP_BIND_FLAG) { data[2] = source[2]; // Set when currying a bound function. newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG; } // Compose partial arguments. var value = source[3]; if (value) { var partials = data[3]; data[3] = partials ? composeArgs(partials, value, source[4]) : value; data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4]; } // Compose partial right arguments. value = source[5]; if (value) { partials = data[5]; data[5] = partials ? composeArgsRight(partials, value, source[6]) : value; data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6]; } // Use source `argPos` if available. value = source[7]; if (value) { data[7] = value; } // Use source `ary` if it's smaller. if (srcBitmask & WRAP_ARY_FLAG) { data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); } // Use source `arity` if one is not provided. if (data[9] == null) { data[9] = source[9]; } // Use source `func` and merge bitmasks. data[0] = source[0]; data[1] = newBitmask; return data; } /** * This function is like * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) * except that it includes inherited enumerable properties. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ function nativeKeysIn(object) { var result = []; if (object != null) { for (var key in Object(object)) { result.push(key); } } return result; } /** * Converts `value` to a string using `Object.prototype.toString`. * * @private * @param {*} value The value to convert. * @returns {string} Returns the converted string. */ function objectToString(value) { return nativeObjectToString.call(value); } /** * A specialized version of `baseRest` which transforms the rest array. * * @private * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @param {Function} transform The rest array transform. * @returns {Function} Returns the new function. */ function overRest(func, start, transform) { start = nativeMax(start === undefined ? (func.length - 1) : start, 0); return function() { var args = arguments, index = -1, length = nativeMax(args.length - start, 0), array = Array(length); while (++index < length) { array[index] = args[start + index]; } index = -1; var otherArgs = Array(start + 1); while (++index < start) { otherArgs[index] = args[index]; } otherArgs[start] = transform(array); return apply(func, this, otherArgs); }; } /** * Gets the parent value at `path` of `object`. * * @private * @param {Object} object The object to query. * @param {Array} path The path to get the parent value of. * @returns {*} Returns the parent value. */ function parent(object, path) { return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1)); } /** * Reorder `array` according to the specified indexes where the element at * the first index is assigned as the first element, the element at * the second index is assigned as the second element, and so on. * * @private * @param {Array} array The array to reorder. * @param {Array} indexes The arranged array indexes. * @returns {Array} Returns `array`. */ function reorder(array, indexes) { var arrLength = array.length, length = nativeMin(indexes.length, arrLength), oldArray = copyArray(array); while (length--) { var index = indexes[length]; array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; } return array; } /** * Sets metadata for `func`. * * **Note:** If this function becomes hot, i.e. is invoked a lot in a short * period of time, it will trip its breaker and transition to an identity * function to avoid garbage collection pauses in V8. See * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070) * for more details. * * @private * @param {Function} func The function to associate metadata with. * @param {*} data The metadata. * @returns {Function} Returns `func`. */ var setData = shortOut(baseSetData); /** * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout). * * @private * @param {Function} func The function to delay. * @param {number} wait The number of milliseconds to delay invocation. * @returns {number|Object} Returns the timer id or timeout object. */ var setTimeout = ctxSetTimeout || function(func, wait) { return root.setTimeout(func, wait); }; /** * Sets the `toString` method of `func` to return `string`. * * @private * @param {Function} func The function to modify. * @param {Function} string The `toString` result. * @returns {Function} Returns `func`. */ var setToString = shortOut(baseSetToString); /** * Sets the `toString` method of `wrapper` to mimic the source of `reference` * with wrapper details in a comment at the top of the source body. * * @private * @param {Function} wrapper The function to modify. * @param {Function} reference The reference function. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @returns {Function} Returns `wrapper`. */ function setWrapToString(wrapper, reference, bitmask) { var source = (reference + ''); return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask))); } /** * Creates a function that'll short out and invoke `identity` instead * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` * milliseconds. * * @private * @param {Function} func The function to restrict. * @returns {Function} Returns the new shortable function. */ function shortOut(func) { var count = 0, lastCalled = 0; return function() { var stamp = nativeNow(), remaining = HOT_SPAN - (stamp - lastCalled); lastCalled = stamp; if (remaining > 0) { if (++count >= HOT_COUNT) { return arguments[0]; } } else { count = 0; } return func.apply(undefined, arguments); }; } /** * A specialized version of `_.shuffle` which mutates and sets the size of `array`. * * @private * @param {Array} array The array to shuffle. * @param {number} [size=array.length] The size of `array`. * @returns {Array} Returns `array`. */ function shuffleSelf(array, size) { var index = -1, length = array.length, lastIndex = length - 1; size = size === undefined ? length : size; while (++index < size) { var rand = baseRandom(index, lastIndex), value = array[rand]; array[rand] = array[index]; array[index] = value; } array.length = size; return array; } /** * Converts `string` to a property path array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the property path array. */ var stringToPath = memoizeCapped(function(string) { var result = []; if (string.charCodeAt(0) === 46 /* . */) { result.push(''); } string.replace(rePropName, function(match, number, quote, subString) { result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match)); }); return result; }); /** * Converts `value` to a string key if it's not a string or symbol. * * @private * @param {*} value The value to inspect. * @returns {string|symbol} Returns the key. */ function toKey(value) { if (typeof value == 'string' || isSymbol(value)) { return value; } var result = (value + ''); return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; } /** * Converts `func` to its source code. * * @private * @param {Function} func The function to convert. * @returns {string} Returns the source code. */ function toSource(func) { if (func != null) { try { return funcToString.call(func); } catch (e) {} try { return (func + ''); } catch (e) {} } return ''; } /** * Updates wrapper `details` based on `bitmask` flags. * * @private * @returns {Array} details The details to modify. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @returns {Array} Returns `details`. */ function updateWrapDetails(details, bitmask) { arrayEach(wrapFlags, function(pair) { var value = '_.' + pair[0]; if ((bitmask & pair[1]) && !arrayIncludes(details, value)) { details.push(value); } }); return details.sort(); } /** * Creates a clone of `wrapper`. * * @private * @param {Object} wrapper The wrapper to clone. * @returns {Object} Returns the cloned wrapper. */ function wrapperClone(wrapper) { if (wrapper instanceof LazyWrapper) { return wrapper.clone(); } var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__); result.__actions__ = copyArray(wrapper.__actions__); result.__index__ = wrapper.__index__; result.__values__ = wrapper.__values__; return result; } /*------------------------------------------------------------------------*/ /** * Creates an array of elements split into groups the length of `size`. * If `array` can't be split evenly, the final chunk will be the remaining * elements. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to process. * @param {number} [size=1] The length of each chunk * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the new array of chunks. * @example * * _.chunk(['a', 'b', 'c', 'd'], 2); * // => [['a', 'b'], ['c', 'd']] * * _.chunk(['a', 'b', 'c', 'd'], 3); * // => [['a', 'b', 'c'], ['d']] */ function chunk(array, size, guard) { if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) { size = 1; } else { size = nativeMax(toInteger(size), 0); } var length = array == null ? 0 : array.length; if (!length || size < 1) { return []; } var index = 0, resIndex = 0, result = Array(nativeCeil(length / size)); while (index < length) { result[resIndex++] = baseSlice(array, index, (index += size)); } return result; } /** * Creates an array with all falsey values removed. The values `false`, `null`, * `0`, `""`, `undefined`, and `NaN` are falsey. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to compact. * @returns {Array} Returns the new array of filtered values. * @example * * _.compact([0, 1, false, 2, '', 3]); * // => [1, 2, 3] */ function compact(array) { var index = -1, length = array == null ? 0 : array.length, resIndex = 0, result = []; while (++index < length) { var value = array[index]; if (value) { result[resIndex++] = value; } } return result; } /** * Creates a new array concatenating `array` with any additional arrays * and/or values. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to concatenate. * @param {...*} [values] The values to concatenate. * @returns {Array} Returns the new concatenated array. * @example * * var array = [1]; * var other = _.concat(array, 2, [3], [[4]]); * * console.log(other); * // => [1, 2, 3, [4]] * * console.log(array); * // => [1] */ function concat() { var length = arguments.length; if (!length) { return []; } var args = Array(length - 1), array = arguments[0], index = length; while (index--) { args[index - 1] = arguments[index]; } return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1)); } /** * Creates an array of `array` values not included in the other given arrays * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. The order and references of result values are * determined by the first array. * * **Note:** Unlike `_.pullAll`, this method returns a new array. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to inspect. * @param {...Array} [values] The values to exclude. * @returns {Array} Returns the new array of filtered values. * @see _.without, _.xor * @example * * _.difference([2, 1], [2, 3]); * // => [1] */ var difference = baseRest(function(array, values) { return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) : []; }); /** * This method is like `_.difference` except that it accepts `iteratee` which * is invoked for each element of `array` and `values` to generate the criterion * by which they're compared. The order and references of result values are * determined by the first array. The iteratee is invoked with one argument: * (value). * * **Note:** Unlike `_.pullAllBy`, this method returns a new array. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {...Array} [values] The values to exclude. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns the new array of filtered values. * @example * * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); * // => [1.2] * * // The `_.property` iteratee shorthand. * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x'); * // => [{ 'x': 2 }] */ var differenceBy = baseRest(function(array, values) { var iteratee = last(values); if (isArrayLikeObject(iteratee)) { iteratee = undefined; } return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)) : []; }); /** * This method is like `_.difference` except that it accepts `comparator` * which is invoked to compare elements of `array` to `values`. The order and * references of result values are determined by the first array. The comparator * is invoked with two arguments: (arrVal, othVal). * * **Note:** Unlike `_.pullAllWith`, this method returns a new array. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {...Array} [values] The values to exclude. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of filtered values. * @example * * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; * * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual); * // => [{ 'x': 2, 'y': 1 }] */ var differenceWith = baseRest(function(array, values) { var comparator = last(values); if (isArrayLikeObject(comparator)) { comparator = undefined; } return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator) : []; }); /** * Creates a slice of `array` with `n` elements dropped from the beginning. * * @static * @memberOf _ * @since 0.5.0 * @category Array * @param {Array} array The array to query. * @param {number} [n=1] The number of elements to drop. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the slice of `array`. * @example * * _.drop([1, 2, 3]); * // => [2, 3] * * _.drop([1, 2, 3], 2); * // => [3] * * _.drop([1, 2, 3], 5); * // => [] * * _.drop([1, 2, 3], 0); * // => [1, 2, 3] */ function drop(array, n, guard) { var length = array == null ? 0 : array.length; if (!length) { return []; } n = (guard || n === undefined) ? 1 : toInteger(n); return baseSlice(array, n < 0 ? 0 : n, length); } /** * Creates a slice of `array` with `n` elements dropped from the end. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {number} [n=1] The number of elements to drop. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the slice of `array`. * @example * * _.dropRight([1, 2, 3]); * // => [1, 2] * * _.dropRight([1, 2, 3], 2); * // => [1] * * _.dropRight([1, 2, 3], 5); * // => [] * * _.dropRight([1, 2, 3], 0); * // => [1, 2, 3] */ function dropRight(array, n, guard) { var length = array == null ? 0 : array.length; if (!length) { return []; } n = (guard || n === undefined) ? 1 : toInteger(n); n = length - n; return baseSlice(array, 0, n < 0 ? 0 : n); } /** * Creates a slice of `array` excluding elements dropped from the end. * Elements are dropped until `predicate` returns falsey. The predicate is * invoked with three arguments: (value, index, array). * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the slice of `array`. * @example * * var users = [ * { 'user': 'barney', 'active': true }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': false } * ]; * * _.dropRightWhile(users, function(o) { return !o.active; }); * // => objects for ['barney'] * * // The `_.matches` iteratee shorthand. * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false }); * // => objects for ['barney', 'fred'] * * // The `_.matchesProperty` iteratee shorthand. * _.dropRightWhile(users, ['active', false]); * // => objects for ['barney'] * * // The `_.property` iteratee shorthand. * _.dropRightWhile(users, 'active'); * // => objects for ['barney', 'fred', 'pebbles'] */ function dropRightWhile(array, predicate) { return (array && array.length) ? baseWhile(array, getIteratee(predicate, 3), true, true) : []; } /** * Creates a slice of `array` excluding elements dropped from the beginning. * Elements are dropped until `predicate` returns falsey. The predicate is * invoked with three arguments: (value, index, array). * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the slice of `array`. * @example * * var users = [ * { 'user': 'barney', 'active': false }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': true } * ]; * * _.dropWhile(users, function(o) { return !o.active; }); * // => objects for ['pebbles'] * * // The `_.matches` iteratee shorthand. * _.dropWhile(users, { 'user': 'barney', 'active': false }); * // => objects for ['fred', 'pebbles'] * * // The `_.matchesProperty` iteratee shorthand. * _.dropWhile(users, ['active', false]); * // => objects for ['pebbles'] * * // The `_.property` iteratee shorthand. * _.dropWhile(users, 'active'); * // => objects for ['barney', 'fred', 'pebbles'] */ function dropWhile(array, predicate) { return (array && array.length) ? baseWhile(array, getIteratee(predicate, 3), true) : []; } /** * Fills elements of `array` with `value` from `start` up to, but not * including, `end`. * * **Note:** This method mutates `array`. * * @static * @memberOf _ * @since 3.2.0 * @category Array * @param {Array} array The array to fill. * @param {*} value The value to fill `array` with. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns `array`. * @example * * var array = [1, 2, 3]; * * _.fill(array, 'a'); * console.log(array); * // => ['a', 'a', 'a'] * * _.fill(Array(3), 2); * // => [2, 2, 2] * * _.fill([4, 6, 8, 10], '*', 1, 3); * // => [4, '*', '*', 10] */ function fill(array, value, start, end) { var length = array == null ? 0 : array.length; if (!length) { return []; } if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { start = 0; end = length; } return baseFill(array, value, start, end); } /** * This method is like `_.find` except that it returns the index of the first * element `predicate` returns truthy for instead of the element itself. * * @static * @memberOf _ * @since 1.1.0 * @category Array * @param {Array} array The array to inspect. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @param {number} [fromIndex=0] The index to search from. * @returns {number} Returns the index of the found element, else `-1`. * @example * * var users = [ * { 'user': 'barney', 'active': false }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': true } * ]; * * _.findIndex(users, function(o) { return o.user == 'barney'; }); * // => 0 * * // The `_.matches` iteratee shorthand. * _.findIndex(users, { 'user': 'fred', 'active': false }); * // => 1 * * // The `_.matchesProperty` iteratee shorthand. * _.findIndex(users, ['active', false]); * // => 0 * * // The `_.property` iteratee shorthand. * _.findIndex(users, 'active'); * // => 2 */ function findIndex(array, predicate, fromIndex) { var length = array == null ? 0 : array.length; if (!length) { return -1; } var index = fromIndex == null ? 0 : toInteger(fromIndex); if (index < 0) { index = nativeMax(length + index, 0); } return baseFindIndex(array, getIteratee(predicate, 3), index); } /** * This method is like `_.findIndex` except that it iterates over elements * of `collection` from right to left. * * @static * @memberOf _ * @since 2.0.0 * @category Array * @param {Array} array The array to inspect. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @param {number} [fromIndex=array.length-1] The index to search from. * @returns {number} Returns the index of the found element, else `-1`. * @example * * var users = [ * { 'user': 'barney', 'active': true }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': false } * ]; * * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; }); * // => 2 * * // The `_.matches` iteratee shorthand. * _.findLastIndex(users, { 'user': 'barney', 'active': true }); * // => 0 * * // The `_.matchesProperty` iteratee shorthand. * _.findLastIndex(users, ['active', false]); * // => 2 * * // The `_.property` iteratee shorthand. * _.findLastIndex(users, 'active'); * // => 0 */ function findLastIndex(array, predicate, fromIndex) { var length = array == null ? 0 : array.length; if (!length) { return -1; } var index = length - 1; if (fromIndex !== undefined) { index = toInteger(fromIndex); index = fromIndex < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1); } return baseFindIndex(array, getIteratee(predicate, 3), index, true); } /** * Flattens `array` a single level deep. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to flatten. * @returns {Array} Returns the new flattened array. * @example * * _.flatten([1, [2, [3, [4]], 5]]); * // => [1, 2, [3, [4]], 5] */ function flatten(array) { var length = array == null ? 0 : array.length; return length ? baseFlatten(array, 1) : []; } /** * Recursively flattens `array`. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to flatten. * @returns {Array} Returns the new flattened array. * @example * * _.flattenDeep([1, [2, [3, [4]], 5]]); * // => [1, 2, 3, 4, 5] */ function flattenDeep(array) { var length = array == null ? 0 : array.length; return length ? baseFlatten(array, INFINITY) : []; } /** * Recursively flatten `array` up to `depth` times. * * @static * @memberOf _ * @since 4.4.0 * @category Array * @param {Array} array The array to flatten. * @param {number} [depth=1] The maximum recursion depth. * @returns {Array} Returns the new flattened array. * @example * * var array = [1, [2, [3, [4]], 5]]; * * _.flattenDepth(array, 1); * // => [1, 2, [3, [4]], 5] * * _.flattenDepth(array, 2); * // => [1, 2, 3, [4], 5] */ function flattenDepth(array, depth) { var length = array == null ? 0 : array.length; if (!length) { return []; } depth = depth === undefined ? 1 : toInteger(depth); return baseFlatten(array, depth); } /** * The inverse of `_.toPairs`; this method returns an object composed * from key-value `pairs`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} pairs The key-value pairs. * @returns {Object} Returns the new object. * @example * * _.fromPairs([['a', 1], ['b', 2]]); * // => { 'a': 1, 'b': 2 } */ function fromPairs(pairs) { var index = -1, length = pairs == null ? 0 : pairs.length, result = {}; while (++index < length) { var pair = pairs[index]; result[pair[0]] = pair[1]; } return result; } /** * Gets the first element of `array`. * * @static * @memberOf _ * @since 0.1.0 * @alias first * @category Array * @param {Array} array The array to query. * @returns {*} Returns the first element of `array`. * @example * * _.head([1, 2, 3]); * // => 1 * * _.head([]); * // => undefined */ function head(array) { return (array && array.length) ? array[0] : undefined; } /** * Gets the index at which the first occurrence of `value` is found in `array` * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. If `fromIndex` is negative, it's used as the * offset from the end of `array`. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} [fromIndex=0] The index to search from. * @returns {number} Returns the index of the matched value, else `-1`. * @example * * _.indexOf([1, 2, 1, 2], 2); * // => 1 * * // Search from the `fromIndex`. * _.indexOf([1, 2, 1, 2], 2, 2); * // => 3 */ function indexOf(array, value, fromIndex) { var length = array == null ? 0 : array.length; if (!length) { return -1; } var index = fromIndex == null ? 0 : toInteger(fromIndex); if (index < 0) { index = nativeMax(length + index, 0); } return baseIndexOf(array, value, index); } /** * Gets all but the last element of `array`. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to query. * @returns {Array} Returns the slice of `array`. * @example * * _.initial([1, 2, 3]); * // => [1, 2] */ function initial(array) { var length = array == null ? 0 : array.length; return length ? baseSlice(array, 0, -1) : []; } /** * Creates an array of unique values that are included in all given arrays * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. The order and references of result values are * determined by the first array. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @returns {Array} Returns the new array of intersecting values. * @example * * _.intersection([2, 1], [2, 3]); * // => [2] */ var intersection = baseRest(function(arrays) { var mapped = arrayMap(arrays, castArrayLikeObject); return (mapped.length && mapped[0] === arrays[0]) ? baseIntersection(mapped) : []; }); /** * This method is like `_.intersection` except that it accepts `iteratee` * which is invoked for each element of each `arrays` to generate the criterion * by which they're compared. The order and references of result values are * determined by the first array. The iteratee is invoked with one argument: * (value). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns the new array of intersecting values. * @example * * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); * // => [2.1] * * // The `_.property` iteratee shorthand. * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); * // => [{ 'x': 1 }] */ var intersectionBy = baseRest(function(arrays) { var iteratee = last(arrays), mapped = arrayMap(arrays, castArrayLikeObject); if (iteratee === last(mapped)) { iteratee = undefined; } else { mapped.pop(); } return (mapped.length && mapped[0] === arrays[0]) ? baseIntersection(mapped, getIteratee(iteratee, 2)) : []; }); /** * This method is like `_.intersection` except that it accepts `comparator` * which is invoked to compare elements of `arrays`. The order and references * of result values are determined by the first array. The comparator is * invoked with two arguments: (arrVal, othVal). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of intersecting values. * @example * * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; * * _.intersectionWith(objects, others, _.isEqual); * // => [{ 'x': 1, 'y': 2 }] */ var intersectionWith = baseRest(function(arrays) { var comparator = last(arrays), mapped = arrayMap(arrays, castArrayLikeObject); comparator = typeof comparator == 'function' ? comparator : undefined; if (comparator) { mapped.pop(); } return (mapped.length && mapped[0] === arrays[0]) ? baseIntersection(mapped, undefined, comparator) : []; }); /** * Converts all elements in `array` into a string separated by `separator`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to convert. * @param {string} [separator=','] The element separator. * @returns {string} Returns the joined string. * @example * * _.join(['a', 'b', 'c'], '~'); * // => 'a~b~c' */ function join(array, separator) { return array == null ? '' : nativeJoin.call(array, separator); } /** * Gets the last element of `array`. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to query. * @returns {*} Returns the last element of `array`. * @example * * _.last([1, 2, 3]); * // => 3 */ function last(array) { var length = array == null ? 0 : array.length; return length ? array[length - 1] : undefined; } /** * This method is like `_.indexOf` except that it iterates over elements of * `array` from right to left. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} [fromIndex=array.length-1] The index to search from. * @returns {number} Returns the index of the matched value, else `-1`. * @example * * _.lastIndexOf([1, 2, 1, 2], 2); * // => 3 * * // Search from the `fromIndex`. * _.lastIndexOf([1, 2, 1, 2], 2, 2); * // => 1 */ function lastIndexOf(array, value, fromIndex) { var length = array == null ? 0 : array.length; if (!length) { return -1; } var index = length; if (fromIndex !== undefined) { index = toInteger(fromIndex); index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1); } return value === value ? strictLastIndexOf(array, value, index) : baseFindIndex(array, baseIsNaN, index, true); } /** * Gets the element at index `n` of `array`. If `n` is negative, the nth * element from the end is returned. * * @static * @memberOf _ * @since 4.11.0 * @category Array * @param {Array} array The array to query. * @param {number} [n=0] The index of the element to return. * @returns {*} Returns the nth element of `array`. * @example * * var array = ['a', 'b', 'c', 'd']; * * _.nth(array, 1); * // => 'b' * * _.nth(array, -2); * // => 'c'; */ function nth(array, n) { return (array && array.length) ? baseNth(array, toInteger(n)) : undefined; } /** * Removes all given values from `array` using * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. * * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove` * to remove elements from an array by predicate. * * @static * @memberOf _ * @since 2.0.0 * @category Array * @param {Array} array The array to modify. * @param {...*} [values] The values to remove. * @returns {Array} Returns `array`. * @example * * var array = ['a', 'b', 'c', 'a', 'b', 'c']; * * _.pull(array, 'a', 'c'); * console.log(array); * // => ['b', 'b'] */ var pull = baseRest(pullAll); /** * This method is like `_.pull` except that it accepts an array of values to remove. * * **Note:** Unlike `_.difference`, this method mutates `array`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to modify. * @param {Array} values The values to remove. * @returns {Array} Returns `array`. * @example * * var array = ['a', 'b', 'c', 'a', 'b', 'c']; * * _.pullAll(array, ['a', 'c']); * console.log(array); * // => ['b', 'b'] */ function pullAll(array, values) { return (array && array.length && values && values.length) ? basePullAll(array, values) : array; } /** * This method is like `_.pullAll` except that it accepts `iteratee` which is * invoked for each element of `array` and `values` to generate the criterion * by which they're compared. The iteratee is invoked with one argument: (value). * * **Note:** Unlike `_.differenceBy`, this method mutates `array`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to modify. * @param {Array} values The values to remove. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns `array`. * @example * * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }]; * * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x'); * console.log(array); * // => [{ 'x': 2 }] */ function pullAllBy(array, values, iteratee) { return (array && array.length && values && values.length) ? basePullAll(array, values, getIteratee(iteratee, 2)) : array; } /** * This method is like `_.pullAll` except that it accepts `comparator` which * is invoked to compare elements of `array` to `values`. The comparator is * invoked with two arguments: (arrVal, othVal). * * **Note:** Unlike `_.differenceWith`, this method mutates `array`. * * @static * @memberOf _ * @since 4.6.0 * @category Array * @param {Array} array The array to modify. * @param {Array} values The values to remove. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns `array`. * @example * * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }]; * * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual); * console.log(array); * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }] */ function pullAllWith(array, values, comparator) { return (array && array.length && values && values.length) ? basePullAll(array, values, undefined, comparator) : array; } /** * Removes elements from `array` corresponding to `indexes` and returns an * array of removed elements. * * **Note:** Unlike `_.at`, this method mutates `array`. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to modify. * @param {...(number|number[])} [indexes] The indexes of elements to remove. * @returns {Array} Returns the new array of removed elements. * @example * * var array = ['a', 'b', 'c', 'd']; * var pulled = _.pullAt(array, [1, 3]); * * console.log(array); * // => ['a', 'c'] * * console.log(pulled); * // => ['b', 'd'] */ var pullAt = flatRest(function(array, indexes) { var length = array == null ? 0 : array.length, result = baseAt(array, indexes); basePullAt(array, arrayMap(indexes, function(index) { return isIndex(index, length) ? +index : index; }).sort(compareAscending)); return result; }); /** * Removes all elements from `array` that `predicate` returns truthy for * and returns an array of the removed elements. The predicate is invoked * with three arguments: (value, index, array). * * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull` * to pull elements from an array by value. * * @static * @memberOf _ * @since 2.0.0 * @category Array * @param {Array} array The array to modify. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the new array of removed elements. * @example * * var array = [1, 2, 3, 4]; * var evens = _.remove(array, function(n) { * return n % 2 == 0; * }); * * console.log(array); * // => [1, 3] * * console.log(evens); * // => [2, 4] */ function remove(array, predicate) { var result = []; if (!(array && array.length)) { return result; } var index = -1, indexes = [], length = array.length; predicate = getIteratee(predicate, 3); while (++index < length) { var value = array[index]; if (predicate(value, index, array)) { result.push(value); indexes.push(index); } } basePullAt(array, indexes); return result; } /** * Reverses `array` so that the first element becomes the last, the second * element becomes the second to last, and so on. * * **Note:** This method mutates `array` and is based on * [`Array#reverse`](https://mdn.io/Array/reverse). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to modify. * @returns {Array} Returns `array`. * @example * * var array = [1, 2, 3]; * * _.reverse(array); * // => [3, 2, 1] * * console.log(array); * // => [3, 2, 1] */ function reverse(array) { return array == null ? array : nativeReverse.call(array); } /** * Creates a slice of `array` from `start` up to, but not including, `end`. * * **Note:** This method is used instead of * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are * returned. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to slice. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the slice of `array`. */ function slice(array, start, end) { var length = array == null ? 0 : array.length; if (!length) { return []; } if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { start = 0; end = length; } else { start = start == null ? 0 : toInteger(start); end = end === undefined ? length : toInteger(end); } return baseSlice(array, start, end); } /** * Uses a binary search to determine the lowest index at which `value` * should be inserted into `array` in order to maintain its sort order. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @returns {number} Returns the index at which `value` should be inserted * into `array`. * @example * * _.sortedIndex([30, 50], 40); * // => 1 */ function sortedIndex(array, value) { return baseSortedIndex(array, value); } /** * This method is like `_.sortedIndex` except that it accepts `iteratee` * which is invoked for `value` and each element of `array` to compute their * sort ranking. The iteratee is invoked with one argument: (value). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {number} Returns the index at which `value` should be inserted * into `array`. * @example * * var objects = [{ 'x': 4 }, { 'x': 5 }]; * * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); * // => 0 * * // The `_.property` iteratee shorthand. * _.sortedIndexBy(objects, { 'x': 4 }, 'x'); * // => 0 */ function sortedIndexBy(array, value, iteratee) { return baseSortedIndexBy(array, value, getIteratee(iteratee, 2)); } /** * This method is like `_.indexOf` except that it performs a binary * search on a sorted `array`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @returns {number} Returns the index of the matched value, else `-1`. * @example * * _.sortedIndexOf([4, 5, 5, 5, 6], 5); * // => 1 */ function sortedIndexOf(array, value) { var length = array == null ? 0 : array.length; if (length) { var index = baseSortedIndex(array, value); if (index < length && eq(array[index], value)) { return index; } } return -1; } /** * This method is like `_.sortedIndex` except that it returns the highest * index at which `value` should be inserted into `array` in order to * maintain its sort order. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @returns {number} Returns the index at which `value` should be inserted * into `array`. * @example * * _.sortedLastIndex([4, 5, 5, 5, 6], 5); * // => 4 */ function sortedLastIndex(array, value) { return baseSortedIndex(array, value, true); } /** * This method is like `_.sortedLastIndex` except that it accepts `iteratee` * which is invoked for `value` and each element of `array` to compute their * sort ranking. The iteratee is invoked with one argument: (value). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {number} Returns the index at which `value` should be inserted * into `array`. * @example * * var objects = [{ 'x': 4 }, { 'x': 5 }]; * * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); * // => 1 * * // The `_.property` iteratee shorthand. * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x'); * // => 1 */ function sortedLastIndexBy(array, value, iteratee) { return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true); } /** * This method is like `_.lastIndexOf` except that it performs a binary * search on a sorted `array`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @returns {number} Returns the index of the matched value, else `-1`. * @example * * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5); * // => 3 */ function sortedLastIndexOf(array, value) { var length = array == null ? 0 : array.length; if (length) { var index = baseSortedIndex(array, value, true) - 1; if (eq(array[index], value)) { return index; } } return -1; } /** * This method is like `_.uniq` except that it's designed and optimized * for sorted arrays. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @returns {Array} Returns the new duplicate free array. * @example * * _.sortedUniq([1, 1, 2]); * // => [1, 2] */ function sortedUniq(array) { return (array && array.length) ? baseSortedUniq(array) : []; } /** * This method is like `_.uniqBy` except that it's designed and optimized * for sorted arrays. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {Function} [iteratee] The iteratee invoked per element. * @returns {Array} Returns the new duplicate free array. * @example * * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor); * // => [1.1, 2.3] */ function sortedUniqBy(array, iteratee) { return (array && array.length) ? baseSortedUniq(array, getIteratee(iteratee, 2)) : []; } /** * Gets all but the first element of `array`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to query. * @returns {Array} Returns the slice of `array`. * @example * * _.tail([1, 2, 3]); * // => [2, 3] */ function tail(array) { var length = array == null ? 0 : array.length; return length ? baseSlice(array, 1, length) : []; } /** * Creates a slice of `array` with `n` elements taken from the beginning. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to query. * @param {number} [n=1] The number of elements to take. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the slice of `array`. * @example * * _.take([1, 2, 3]); * // => [1] * * _.take([1, 2, 3], 2); * // => [1, 2] * * _.take([1, 2, 3], 5); * // => [1, 2, 3] * * _.take([1, 2, 3], 0); * // => [] */ function take(array, n, guard) { if (!(array && array.length)) { return []; } n = (guard || n === undefined) ? 1 : toInteger(n); return baseSlice(array, 0, n < 0 ? 0 : n); } /** * Creates a slice of `array` with `n` elements taken from the end. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {number} [n=1] The number of elements to take. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the slice of `array`. * @example * * _.takeRight([1, 2, 3]); * // => [3] * * _.takeRight([1, 2, 3], 2); * // => [2, 3] * * _.takeRight([1, 2, 3], 5); * // => [1, 2, 3] * * _.takeRight([1, 2, 3], 0); * // => [] */ function takeRight(array, n, guard) { var length = array == null ? 0 : array.length; if (!length) { return []; } n = (guard || n === undefined) ? 1 : toInteger(n); n = length - n; return baseSlice(array, n < 0 ? 0 : n, length); } /** * Creates a slice of `array` with elements taken from the end. Elements are * taken until `predicate` returns falsey. The predicate is invoked with * three arguments: (value, index, array). * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the slice of `array`. * @example * * var users = [ * { 'user': 'barney', 'active': true }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': false } * ]; * * _.takeRightWhile(users, function(o) { return !o.active; }); * // => objects for ['fred', 'pebbles'] * * // The `_.matches` iteratee shorthand. * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false }); * // => objects for ['pebbles'] * * // The `_.matchesProperty` iteratee shorthand. * _.takeRightWhile(users, ['active', false]); * // => objects for ['fred', 'pebbles'] * * // The `_.property` iteratee shorthand. * _.takeRightWhile(users, 'active'); * // => [] */ function takeRightWhile(array, predicate) { return (array && array.length) ? baseWhile(array, getIteratee(predicate, 3), false, true) : []; } /** * Creates a slice of `array` with elements taken from the beginning. Elements * are taken until `predicate` returns falsey. The predicate is invoked with * three arguments: (value, index, array). * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the slice of `array`. * @example * * var users = [ * { 'user': 'barney', 'active': false }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': true } * ]; * * _.takeWhile(users, function(o) { return !o.active; }); * // => objects for ['barney', 'fred'] * * // The `_.matches` iteratee shorthand. * _.takeWhile(users, { 'user': 'barney', 'active': false }); * // => objects for ['barney'] * * // The `_.matchesProperty` iteratee shorthand. * _.takeWhile(users, ['active', false]); * // => objects for ['barney', 'fred'] * * // The `_.property` iteratee shorthand. * _.takeWhile(users, 'active'); * // => [] */ function takeWhile(array, predicate) { return (array && array.length) ? baseWhile(array, getIteratee(predicate, 3)) : []; } /** * Creates an array of unique values, in order, from all given arrays using * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @returns {Array} Returns the new array of combined values. * @example * * _.union([2], [1, 2]); * // => [2, 1] */ var union = baseRest(function(arrays) { return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true)); }); /** * This method is like `_.union` except that it accepts `iteratee` which is * invoked for each element of each `arrays` to generate the criterion by * which uniqueness is computed. Result values are chosen from the first * array in which the value occurs. The iteratee is invoked with one argument: * (value). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns the new array of combined values. * @example * * _.unionBy([2.1], [1.2, 2.3], Math.floor); * // => [2.1, 1.2] * * // The `_.property` iteratee shorthand. * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); * // => [{ 'x': 1 }, { 'x': 2 }] */ var unionBy = baseRest(function(arrays) { var iteratee = last(arrays); if (isArrayLikeObject(iteratee)) { iteratee = undefined; } return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)); }); /** * This method is like `_.union` except that it accepts `comparator` which * is invoked to compare elements of `arrays`. Result values are chosen from * the first array in which the value occurs. The comparator is invoked * with two arguments: (arrVal, othVal). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of combined values. * @example * * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; * * _.unionWith(objects, others, _.isEqual); * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] */ var unionWith = baseRest(function(arrays) { var comparator = last(arrays); comparator = typeof comparator == 'function' ? comparator : undefined; return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator); }); /** * Creates a duplicate-free version of an array, using * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons, in which only the first occurrence of each element * is kept. The order of result values is determined by the order they occur * in the array. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to inspect. * @returns {Array} Returns the new duplicate free array. * @example * * _.uniq([2, 1, 2]); * // => [2, 1] */ function uniq(array) { return (array && array.length) ? baseUniq(array) : []; } /** * This method is like `_.uniq` except that it accepts `iteratee` which is * invoked for each element in `array` to generate the criterion by which * uniqueness is computed. The order of result values is determined by the * order they occur in the array. The iteratee is invoked with one argument: * (value). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns the new duplicate free array. * @example * * _.uniqBy([2.1, 1.2, 2.3], Math.floor); * // => [2.1, 1.2] * * // The `_.property` iteratee shorthand. * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); * // => [{ 'x': 1 }, { 'x': 2 }] */ function uniqBy(array, iteratee) { return (array && array.length) ? baseUniq(array, getIteratee(iteratee, 2)) : []; } /** * This method is like `_.uniq` except that it accepts `comparator` which * is invoked to compare elements of `array`. The order of result values is * determined by the order they occur in the array.The comparator is invoked * with two arguments: (arrVal, othVal). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new duplicate free array. * @example * * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }]; * * _.uniqWith(objects, _.isEqual); * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] */ function uniqWith(array, comparator) { comparator = typeof comparator == 'function' ? comparator : undefined; return (array && array.length) ? baseUniq(array, undefined, comparator) : []; } /** * This method is like `_.zip` except that it accepts an array of grouped * elements and creates an array regrouping the elements to their pre-zip * configuration. * * @static * @memberOf _ * @since 1.2.0 * @category Array * @param {Array} array The array of grouped elements to process. * @returns {Array} Returns the new array of regrouped elements. * @example * * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]); * // => [['a', 1, true], ['b', 2, false]] * * _.unzip(zipped); * // => [['a', 'b'], [1, 2], [true, false]] */ function unzip(array) { if (!(array && array.length)) { return []; } var length = 0; array = arrayFilter(array, function(group) { if (isArrayLikeObject(group)) { length = nativeMax(group.length, length); return true; } }); return baseTimes(length, function(index) { return arrayMap(array, baseProperty(index)); }); } /** * This method is like `_.unzip` except that it accepts `iteratee` to specify * how regrouped values should be combined. The iteratee is invoked with the * elements of each group: (...group). * * @static * @memberOf _ * @since 3.8.0 * @category Array * @param {Array} array The array of grouped elements to process. * @param {Function} [iteratee=_.identity] The function to combine * regrouped values. * @returns {Array} Returns the new array of regrouped elements. * @example * * var zipped = _.zip([1, 2], [10, 20], [100, 200]); * // => [[1, 10, 100], [2, 20, 200]] * * _.unzipWith(zipped, _.add); * // => [3, 30, 300] */ function unzipWith(array, iteratee) { if (!(array && array.length)) { return []; } var result = unzip(array); if (iteratee == null) { return result; } return arrayMap(result, function(group) { return apply(iteratee, undefined, group); }); } /** * Creates an array excluding all given values using * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. * * **Note:** Unlike `_.pull`, this method returns a new array. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to inspect. * @param {...*} [values] The values to exclude. * @returns {Array} Returns the new array of filtered values. * @see _.difference, _.xor * @example * * _.without([2, 1, 2, 3], 1, 2); * // => [3] */ var without = baseRest(function(array, values) { return isArrayLikeObject(array) ? baseDifference(array, values) : []; }); /** * Creates an array of unique values that is the * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) * of the given arrays. The order of result values is determined by the order * they occur in the arrays. * * @static * @memberOf _ * @since 2.4.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @returns {Array} Returns the new array of filtered values. * @see _.difference, _.without * @example * * _.xor([2, 1], [2, 3]); * // => [1, 3] */ var xor = baseRest(function(arrays) { return baseXor(arrayFilter(arrays, isArrayLikeObject)); }); /** * This method is like `_.xor` except that it accepts `iteratee` which is * invoked for each element of each `arrays` to generate the criterion by * which by which they're compared. The order of result values is determined * by the order they occur in the arrays. The iteratee is invoked with one * argument: (value). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns the new array of filtered values. * @example * * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor); * // => [1.2, 3.4] * * // The `_.property` iteratee shorthand. * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); * // => [{ 'x': 2 }] */ var xorBy = baseRest(function(arrays) { var iteratee = last(arrays); if (isArrayLikeObject(iteratee)) { iteratee = undefined; } return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2)); }); /** * This method is like `_.xor` except that it accepts `comparator` which is * invoked to compare elements of `arrays`. The order of result values is * determined by the order they occur in the arrays. The comparator is invoked * with two arguments: (arrVal, othVal). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of filtered values. * @example * * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; * * _.xorWith(objects, others, _.isEqual); * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] */ var xorWith = baseRest(function(arrays) { var comparator = last(arrays); comparator = typeof comparator == 'function' ? comparator : undefined; return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator); }); /** * Creates an array of grouped elements, the first of which contains the * first elements of the given arrays, the second of which contains the * second elements of the given arrays, and so on. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {...Array} [arrays] The arrays to process. * @returns {Array} Returns the new array of grouped elements. * @example * * _.zip(['a', 'b'], [1, 2], [true, false]); * // => [['a', 1, true], ['b', 2, false]] */ var zip = baseRest(unzip); /** * This method is like `_.fromPairs` except that it accepts two arrays, * one of property identifiers and one of corresponding values. * * @static * @memberOf _ * @since 0.4.0 * @category Array * @param {Array} [props=[]] The property identifiers. * @param {Array} [values=[]] The property values. * @returns {Object} Returns the new object. * @example * * _.zipObject(['a', 'b'], [1, 2]); * // => { 'a': 1, 'b': 2 } */ function zipObject(props, values) { return baseZipObject(props || [], values || [], assignValue); } /** * This method is like `_.zipObject` except that it supports property paths. * * @static * @memberOf _ * @since 4.1.0 * @category Array * @param {Array} [props=[]] The property identifiers. * @param {Array} [values=[]] The property values. * @returns {Object} Returns the new object. * @example * * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]); * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } } */ function zipObjectDeep(props, values) { return baseZipObject(props || [], values || [], baseSet); } /** * This method is like `_.zip` except that it accepts `iteratee` to specify * how grouped values should be combined. The iteratee is invoked with the * elements of each group: (...group). * * @static * @memberOf _ * @since 3.8.0 * @category Array * @param {...Array} [arrays] The arrays to process. * @param {Function} [iteratee=_.identity] The function to combine * grouped values. * @returns {Array} Returns the new array of grouped elements. * @example * * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) { * return a + b + c; * }); * // => [111, 222] */ var zipWith = baseRest(function(arrays) { var length = arrays.length, iteratee = length > 1 ? arrays[length - 1] : undefined; iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined; return unzipWith(arrays, iteratee); }); /*------------------------------------------------------------------------*/ /** * Creates a `lodash` wrapper instance that wraps `value` with explicit method * chain sequences enabled. The result of such sequences must be unwrapped * with `_#value`. * * @static * @memberOf _ * @since 1.3.0 * @category Seq * @param {*} value The value to wrap. * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 }, * { 'user': 'pebbles', 'age': 1 } * ]; * * var youngest = _ * .chain(users) * .sortBy('age') * .map(function(o) { * return o.user + ' is ' + o.age; * }) * .head() * .value(); * // => 'pebbles is 1' */ function chain(value) { var result = lodash(value); result.__chain__ = true; return result; } /** * This method invokes `interceptor` and returns `value`. The interceptor * is invoked with one argument; (value). The purpose of this method is to * "tap into" a method chain sequence in order to modify intermediate results. * * @static * @memberOf _ * @since 0.1.0 * @category Seq * @param {*} value The value to provide to `interceptor`. * @param {Function} interceptor The function to invoke. * @returns {*} Returns `value`. * @example * * _([1, 2, 3]) * .tap(function(array) { * // Mutate input array. * array.pop(); * }) * .reverse() * .value(); * // => [2, 1] */ function tap(value, interceptor) { interceptor(value); return value; } /** * This method is like `_.tap` except that it returns the result of `interceptor`. * The purpose of this method is to "pass thru" values replacing intermediate * results in a method chain sequence. * * @static * @memberOf _ * @since 3.0.0 * @category Seq * @param {*} value The value to provide to `interceptor`. * @param {Function} interceptor The function to invoke. * @returns {*} Returns the result of `interceptor`. * @example * * _(' abc ') * .chain() * .trim() * .thru(function(value) { * return [value]; * }) * .value(); * // => ['abc'] */ function thru(value, interceptor) { return interceptor(value); } /** * This method is the wrapper version of `_.at`. * * @name at * @memberOf _ * @since 1.0.0 * @category Seq * @param {...(string|string[])} [paths] The property paths to pick. * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; * * _(object).at(['a[0].b.c', 'a[1]']).value(); * // => [3, 4] */ var wrapperAt = flatRest(function(paths) { var length = paths.length, start = length ? paths[0] : 0, value = this.__wrapped__, interceptor = function(object) { return baseAt(object, paths); }; if (length > 1 || this.__actions__.length || !(value instanceof LazyWrapper) || !isIndex(start)) { return this.thru(interceptor); } value = value.slice(start, +start + (length ? 1 : 0)); value.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined }); return new LodashWrapper(value, this.__chain__).thru(function(array) { if (length && !array.length) { array.push(undefined); } return array; }); }); /** * Creates a `lodash` wrapper instance with explicit method chain sequences enabled. * * @name chain * @memberOf _ * @since 0.1.0 * @category Seq * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 } * ]; * * // A sequence without explicit chaining. * _(users).head(); * // => { 'user': 'barney', 'age': 36 } * * // A sequence with explicit chaining. * _(users) * .chain() * .head() * .pick('user') * .value(); * // => { 'user': 'barney' } */ function wrapperChain() { return chain(this); } /** * Executes the chain sequence and returns the wrapped result. * * @name commit * @memberOf _ * @since 3.2.0 * @category Seq * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * var array = [1, 2]; * var wrapped = _(array).push(3); * * console.log(array); * // => [1, 2] * * wrapped = wrapped.commit(); * console.log(array); * // => [1, 2, 3] * * wrapped.last(); * // => 3 * * console.log(array); * // => [1, 2, 3] */ function wrapperCommit() { return new LodashWrapper(this.value(), this.__chain__); } /** * Gets the next value on a wrapped object following the * [iterator protocol](https://mdn.io/iteration_protocols#iterator). * * @name next * @memberOf _ * @since 4.0.0 * @category Seq * @returns {Object} Returns the next iterator value. * @example * * var wrapped = _([1, 2]); * * wrapped.next(); * // => { 'done': false, 'value': 1 } * * wrapped.next(); * // => { 'done': false, 'value': 2 } * * wrapped.next(); * // => { 'done': true, 'value': undefined } */ function wrapperNext() { if (this.__values__ === undefined) { this.__values__ = toArray(this.value()); } var done = this.__index__ >= this.__values__.length, value = done ? undefined : this.__values__[this.__index__++]; return { 'done': done, 'value': value }; } /** * Enables the wrapper to be iterable. * * @name Symbol.iterator * @memberOf _ * @since 4.0.0 * @category Seq * @returns {Object} Returns the wrapper object. * @example * * var wrapped = _([1, 2]); * * wrapped[Symbol.iterator]() === wrapped; * // => true * * Array.from(wrapped); * // => [1, 2] */ function wrapperToIterator() { return this; } /** * Creates a clone of the chain sequence planting `value` as the wrapped value. * * @name plant * @memberOf _ * @since 3.2.0 * @category Seq * @param {*} value The value to plant. * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * function square(n) { * return n * n; * } * * var wrapped = _([1, 2]).map(square); * var other = wrapped.plant([3, 4]); * * other.value(); * // => [9, 16] * * wrapped.value(); * // => [1, 4] */ function wrapperPlant(value) { var result, parent = this; while (parent instanceof baseLodash) { var clone = wrapperClone(parent); clone.__index__ = 0; clone.__values__ = undefined; if (result) { previous.__wrapped__ = clone; } else { result = clone; } var previous = clone; parent = parent.__wrapped__; } previous.__wrapped__ = value; return result; } /** * This method is the wrapper version of `_.reverse`. * * **Note:** This method mutates the wrapped array. * * @name reverse * @memberOf _ * @since 0.1.0 * @category Seq * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * var array = [1, 2, 3]; * * _(array).reverse().value() * // => [3, 2, 1] * * console.log(array); * // => [3, 2, 1] */ function wrapperReverse() { var value = this.__wrapped__; if (value instanceof LazyWrapper) { var wrapped = value; if (this.__actions__.length) { wrapped = new LazyWrapper(this); } wrapped = wrapped.reverse(); wrapped.__actions__.push({ 'func': thru, 'args': [reverse], 'thisArg': undefined }); return new LodashWrapper(wrapped, this.__chain__); } return this.thru(reverse); } /** * Executes the chain sequence to resolve the unwrapped value. * * @name value * @memberOf _ * @since 0.1.0 * @alias toJSON, valueOf * @category Seq * @returns {*} Returns the resolved unwrapped value. * @example * * _([1, 2, 3]).value(); * // => [1, 2, 3] */ function wrapperValue() { return baseWrapperValue(this.__wrapped__, this.__actions__); } /*------------------------------------------------------------------------*/ /** * Creates an object composed of keys generated from the results of running * each element of `collection` thru `iteratee`. The corresponding value of * each key is the number of times the key was returned by `iteratee`. The * iteratee is invoked with one argument: (value). * * @static * @memberOf _ * @since 0.5.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The iteratee to transform keys. * @returns {Object} Returns the composed aggregate object. * @example * * _.countBy([6.1, 4.2, 6.3], Math.floor); * // => { '4': 1, '6': 2 } * * // The `_.property` iteratee shorthand. * _.countBy(['one', 'two', 'three'], 'length'); * // => { '3': 2, '5': 1 } */ var countBy = createAggregator(function(result, value, key) { if (hasOwnProperty.call(result, key)) { ++result[key]; } else { baseAssignValue(result, key, 1); } }); /** * Checks if `predicate` returns truthy for **all** elements of `collection`. * Iteration is stopped once `predicate` returns falsey. The predicate is * invoked with three arguments: (value, index|key, collection). * * **Note:** This method returns `true` for * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of * elements of empty collections. * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {boolean} Returns `true` if all elements pass the predicate check, * else `false`. * @example * * _.every([true, 1, null, 'yes'], Boolean); * // => false * * var users = [ * { 'user': 'barney', 'age': 36, 'active': false }, * { 'user': 'fred', 'age': 40, 'active': false } * ]; * * // The `_.matches` iteratee shorthand. * _.every(users, { 'user': 'barney', 'active': false }); * // => false * * // The `_.matchesProperty` iteratee shorthand. * _.every(users, ['active', false]); * // => true * * // The `_.property` iteratee shorthand. * _.every(users, 'active'); * // => false */ function every(collection, predicate, guard) { var func = isArray(collection) ? arrayEvery : baseEvery; if (guard && isIterateeCall(collection, predicate, guard)) { predicate = undefined; } return func(collection, getIteratee(predicate, 3)); } /** * Iterates over elements of `collection`, returning an array of all elements * `predicate` returns truthy for. The predicate is invoked with three * arguments: (value, index|key, collection). * * **Note:** Unlike `_.remove`, this method returns a new array. * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the new filtered array. * @see _.reject * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'active': true }, * { 'user': 'fred', 'age': 40, 'active': false } * ]; * * _.filter(users, function(o) { return !o.active; }); * // => objects for ['fred'] * * // The `_.matches` iteratee shorthand. * _.filter(users, { 'age': 36, 'active': true }); * // => objects for ['barney'] * * // The `_.matchesProperty` iteratee shorthand. * _.filter(users, ['active', false]); * // => objects for ['fred'] * * // The `_.property` iteratee shorthand. * _.filter(users, 'active'); * // => objects for ['barney'] */ function filter(collection, predicate) { var func = isArray(collection) ? arrayFilter : baseFilter; return func(collection, getIteratee(predicate, 3)); } /** * Iterates over elements of `collection`, returning the first element * `predicate` returns truthy for. The predicate is invoked with three * arguments: (value, index|key, collection). * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to inspect. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @param {number} [fromIndex=0] The index to search from. * @returns {*} Returns the matched element, else `undefined`. * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'active': true }, * { 'user': 'fred', 'age': 40, 'active': false }, * { 'user': 'pebbles', 'age': 1, 'active': true } * ]; * * _.find(users, function(o) { return o.age < 40; }); * // => object for 'barney' * * // The `_.matches` iteratee shorthand. * _.find(users, { 'age': 1, 'active': true }); * // => object for 'pebbles' * * // The `_.matchesProperty` iteratee shorthand. * _.find(users, ['active', false]); * // => object for 'fred' * * // The `_.property` iteratee shorthand. * _.find(users, 'active'); * // => object for 'barney' */ var find = createFind(findIndex); /** * This method is like `_.find` except that it iterates over elements of * `collection` from right to left. * * @static * @memberOf _ * @since 2.0.0 * @category Collection * @param {Array|Object} collection The collection to inspect. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @param {number} [fromIndex=collection.length-1] The index to search from. * @returns {*} Returns the matched element, else `undefined`. * @example * * _.findLast([1, 2, 3, 4], function(n) { * return n % 2 == 1; * }); * // => 3 */ var findLast = createFind(findLastIndex); /** * Creates a flattened array of values by running each element in `collection` * thru `iteratee` and flattening the mapped results. The iteratee is invoked * with three arguments: (value, index|key, collection). * * @static * @memberOf _ * @since 4.0.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Array} Returns the new flattened array. * @example * * function duplicate(n) { * return [n, n]; * } * * _.flatMap([1, 2], duplicate); * // => [1, 1, 2, 2] */ function flatMap(collection, iteratee) { return baseFlatten(map(collection, iteratee), 1); } /** * This method is like `_.flatMap` except that it recursively flattens the * mapped results. * * @static * @memberOf _ * @since 4.7.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Array} Returns the new flattened array. * @example * * function duplicate(n) { * return [[[n, n]]]; * } * * _.flatMapDeep([1, 2], duplicate); * // => [1, 1, 2, 2] */ function flatMapDeep(collection, iteratee) { return baseFlatten(map(collection, iteratee), INFINITY); } /** * This method is like `_.flatMap` except that it recursively flattens the * mapped results up to `depth` times. * * @static * @memberOf _ * @since 4.7.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {number} [depth=1] The maximum recursion depth. * @returns {Array} Returns the new flattened array. * @example * * function duplicate(n) { * return [[[n, n]]]; * } * * _.flatMapDepth([1, 2], duplicate, 2); * // => [[1, 1], [2, 2]] */ function flatMapDepth(collection, iteratee, depth) { depth = depth === undefined ? 1 : toInteger(depth); return baseFlatten(map(collection, iteratee), depth); } /** * Iterates over elements of `collection` and invokes `iteratee` for each element. * The iteratee is invoked with three arguments: (value, index|key, collection). * Iteratee functions may exit iteration early by explicitly returning `false`. * * **Note:** As with other "Collections" methods, objects with a "length" * property are iterated like arrays. To avoid this behavior use `_.forIn` * or `_.forOwn` for object iteration. * * @static * @memberOf _ * @since 0.1.0 * @alias each * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Array|Object} Returns `collection`. * @see _.forEachRight * @example * * _.forEach([1, 2], function(value) { * console.log(value); * }); * // => Logs `1` then `2`. * * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) { * console.log(key); * }); * // => Logs 'a' then 'b' (iteration order is not guaranteed). */ function forEach(collection, iteratee) { var func = isArray(collection) ? arrayEach : baseEach; return func(collection, getIteratee(iteratee, 3)); } /** * This method is like `_.forEach` except that it iterates over elements of * `collection` from right to left. * * @static * @memberOf _ * @since 2.0.0 * @alias eachRight * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Array|Object} Returns `collection`. * @see _.forEach * @example * * _.forEachRight([1, 2], function(value) { * console.log(value); * }); * // => Logs `2` then `1`. */ function forEachRight(collection, iteratee) { var func = isArray(collection) ? arrayEachRight : baseEachRight; return func(collection, getIteratee(iteratee, 3)); } /** * Creates an object composed of keys generated from the results of running * each element of `collection` thru `iteratee`. The order of grouped values * is determined by the order they occur in `collection`. The corresponding * value of each key is an array of elements responsible for generating the * key. The iteratee is invoked with one argument: (value). * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The iteratee to transform keys. * @returns {Object} Returns the composed aggregate object. * @example * * _.groupBy([6.1, 4.2, 6.3], Math.floor); * // => { '4': [4.2], '6': [6.1, 6.3] } * * // The `_.property` iteratee shorthand. * _.groupBy(['one', 'two', 'three'], 'length'); * // => { '3': ['one', 'two'], '5': ['three'] } */ var groupBy = createAggregator(function(result, value, key) { if (hasOwnProperty.call(result, key)) { result[key].push(value); } else { baseAssignValue(result, key, [value]); } }); /** * Checks if `value` is in `collection`. If `collection` is a string, it's * checked for a substring of `value`, otherwise * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * is used for equality comparisons. If `fromIndex` is negative, it's used as * the offset from the end of `collection`. * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object|string} collection The collection to inspect. * @param {*} value The value to search for. * @param {number} [fromIndex=0] The index to search from. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. * @returns {boolean} Returns `true` if `value` is found, else `false`. * @example * * _.includes([1, 2, 3], 1); * // => true * * _.includes([1, 2, 3], 1, 2); * // => false * * _.includes({ 'a': 1, 'b': 2 }, 1); * // => true * * _.includes('abcd', 'bc'); * // => true */ function includes(collection, value, fromIndex, guard) { collection = isArrayLike(collection) ? collection : values(collection); fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0; var length = collection.length; if (fromIndex < 0) { fromIndex = nativeMax(length + fromIndex, 0); } return isString(collection) ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1) : (!!length && baseIndexOf(collection, value, fromIndex) > -1); } /** * Invokes the method at `path` of each element in `collection`, returning * an array of the results of each invoked method. Any additional arguments * are provided to each invoked method. If `path` is a function, it's invoked * for, and `this` bound to, each element in `collection`. * * @static * @memberOf _ * @since 4.0.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Array|Function|string} path The path of the method to invoke or * the function invoked per iteration. * @param {...*} [args] The arguments to invoke each method with. * @returns {Array} Returns the array of results. * @example * * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort'); * // => [[1, 5, 7], [1, 2, 3]] * * _.invokeMap([123, 456], String.prototype.split, ''); * // => [['1', '2', '3'], ['4', '5', '6']] */ var invokeMap = baseRest(function(collection, path, args) { var index = -1, isFunc = typeof path == 'function', result = isArrayLike(collection) ? Array(collection.length) : []; baseEach(collection, function(value) { result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args); }); return result; }); /** * Creates an object composed of keys generated from the results of running * each element of `collection` thru `iteratee`. The corresponding value of * each key is the last element responsible for generating the key. The * iteratee is invoked with one argument: (value). * * @static * @memberOf _ * @since 4.0.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The iteratee to transform keys. * @returns {Object} Returns the composed aggregate object. * @example * * var array = [ * { 'dir': 'left', 'code': 97 }, * { 'dir': 'right', 'code': 100 } * ]; * * _.keyBy(array, function(o) { * return String.fromCharCode(o.code); * }); * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } * * _.keyBy(array, 'dir'); * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } */ var keyBy = createAggregator(function(result, value, key) { baseAssignValue(result, key, value); }); /** * Creates an array of values by running each element in `collection` thru * `iteratee`. The iteratee is invoked with three arguments: * (value, index|key, collection). * * Many lodash methods are guarded to work as iteratees for methods like * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. * * The guarded methods are: * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`, * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`, * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`, * `template`, `trim`, `trimEnd`, `trimStart`, and `words` * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Array} Returns the new mapped array. * @example * * function square(n) { * return n * n; * } * * _.map([4, 8], square); * // => [16, 64] * * _.map({ 'a': 4, 'b': 8 }, square); * // => [16, 64] (iteration order is not guaranteed) * * var users = [ * { 'user': 'barney' }, * { 'user': 'fred' } * ]; * * // The `_.property` iteratee shorthand. * _.map(users, 'user'); * // => ['barney', 'fred'] */ function map(collection, iteratee) { var func = isArray(collection) ? arrayMap : baseMap; return func(collection, getIteratee(iteratee, 3)); } /** * This method is like `_.sortBy` except that it allows specifying the sort * orders of the iteratees to sort by. If `orders` is unspecified, all values * are sorted in ascending order. Otherwise, specify an order of "desc" for * descending or "asc" for ascending sort order of corresponding values. * * @static * @memberOf _ * @since 4.0.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]] * The iteratees to sort by. * @param {string[]} [orders] The sort orders of `iteratees`. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. * @returns {Array} Returns the new sorted array. * @example * * var users = [ * { 'user': 'fred', 'age': 48 }, * { 'user': 'barney', 'age': 34 }, * { 'user': 'fred', 'age': 40 }, * { 'user': 'barney', 'age': 36 } * ]; * * // Sort by `user` in ascending order and by `age` in descending order. * _.orderBy(users, ['user', 'age'], ['asc', 'desc']); * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] */ function orderBy(collection, iteratees, orders, guard) { if (collection == null) { return []; } if (!isArray(iteratees)) { iteratees = iteratees == null ? [] : [iteratees]; } orders = guard ? undefined : orders; if (!isArray(orders)) { orders = orders == null ? [] : [orders]; } return baseOrderBy(collection, iteratees, orders); } /** * Creates an array of elements split into two groups, the first of which * contains elements `predicate` returns truthy for, the second of which * contains elements `predicate` returns falsey for. The predicate is * invoked with one argument: (value). * * @static * @memberOf _ * @since 3.0.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the array of grouped elements. * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'active': false }, * { 'user': 'fred', 'age': 40, 'active': true }, * { 'user': 'pebbles', 'age': 1, 'active': false } * ]; * * _.partition(users, function(o) { return o.active; }); * // => objects for [['fred'], ['barney', 'pebbles']] * * // The `_.matches` iteratee shorthand. * _.partition(users, { 'age': 1, 'active': false }); * // => objects for [['pebbles'], ['barney', 'fred']] * * // The `_.matchesProperty` iteratee shorthand. * _.partition(users, ['active', false]); * // => objects for [['barney', 'pebbles'], ['fred']] * * // The `_.property` iteratee shorthand. * _.partition(users, 'active'); * // => objects for [['fred'], ['barney', 'pebbles']] */ var partition = createAggregator(function(result, value, key) { result[key ? 0 : 1].push(value); }, function() { return [[], []]; }); /** * Reduces `collection` to a value which is the accumulated result of running * each element in `collection` thru `iteratee`, where each successive * invocation is supplied the return value of the previous. If `accumulator` * is not given, the first element of `collection` is used as the initial * value. The iteratee is invoked with four arguments: * (accumulator, value, index|key, collection). * * Many lodash methods are guarded to work as iteratees for methods like * `_.reduce`, `_.reduceRight`, and `_.transform`. * * The guarded methods are: * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`, * and `sortBy` * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [accumulator] The initial value. * @returns {*} Returns the accumulated value. * @see _.reduceRight * @example * * _.reduce([1, 2], function(sum, n) { * return sum + n; * }, 0); * // => 3 * * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { * (result[value] || (result[value] = [])).push(key); * return result; * }, {}); * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed) */ function reduce(collection, iteratee, accumulator) { var func = isArray(collection) ? arrayReduce : baseReduce, initAccum = arguments.length < 3; return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach); } /** * This method is like `_.reduce` except that it iterates over elements of * `collection` from right to left. * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [accumulator] The initial value. * @returns {*} Returns the accumulated value. * @see _.reduce * @example * * var array = [[0, 1], [2, 3], [4, 5]]; * * _.reduceRight(array, function(flattened, other) { * return flattened.concat(other); * }, []); * // => [4, 5, 2, 3, 0, 1] */ function reduceRight(collection, iteratee, accumulator) { var func = isArray(collection) ? arrayReduceRight : baseReduce, initAccum = arguments.length < 3; return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight); } /** * The opposite of `_.filter`; this method returns the elements of `collection` * that `predicate` does **not** return truthy for. * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the new filtered array. * @see _.filter * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'active': false }, * { 'user': 'fred', 'age': 40, 'active': true } * ]; * * _.reject(users, function(o) { return !o.active; }); * // => objects for ['fred'] * * // The `_.matches` iteratee shorthand. * _.reject(users, { 'age': 40, 'active': true }); * // => objects for ['barney'] * * // The `_.matchesProperty` iteratee shorthand. * _.reject(users, ['active', false]); * // => objects for ['fred'] * * // The `_.property` iteratee shorthand. * _.reject(users, 'active'); * // => objects for ['barney'] */ function reject(collection, predicate) { var func = isArray(collection) ? arrayFilter : baseFilter; return func(collection, negate(getIteratee(predicate, 3))); } /** * Gets a random element from `collection`. * * @static * @memberOf _ * @since 2.0.0 * @category Collection * @param {Array|Object} collection The collection to sample. * @returns {*} Returns the random element. * @example * * _.sample([1, 2, 3, 4]); * // => 2 */ function sample(collection) { var func = isArray(collection) ? arraySample : baseSample; return func(collection); } /** * Gets `n` random elements at unique keys from `collection` up to the * size of `collection`. * * @static * @memberOf _ * @since 4.0.0 * @category Collection * @param {Array|Object} collection The collection to sample. * @param {number} [n=1] The number of elements to sample. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the random elements. * @example * * _.sampleSize([1, 2, 3], 2); * // => [3, 1] * * _.sampleSize([1, 2, 3], 4); * // => [2, 3, 1] */ function sampleSize(collection, n, guard) { if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) { n = 1; } else { n = toInteger(n); } var func = isArray(collection) ? arraySampleSize : baseSampleSize; return func(collection, n); } /** * Creates an array of shuffled values, using a version of the * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to shuffle. * @returns {Array} Returns the new shuffled array. * @example * * _.shuffle([1, 2, 3, 4]); * // => [4, 1, 3, 2] */ function shuffle(collection) { var func = isArray(collection) ? arrayShuffle : baseShuffle; return func(collection); } /** * Gets the size of `collection` by returning its length for array-like * values or the number of own enumerable string keyed properties for objects. * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object|string} collection The collection to inspect. * @returns {number} Returns the collection size. * @example * * _.size([1, 2, 3]); * // => 3 * * _.size({ 'a': 1, 'b': 2 }); * // => 2 * * _.size('pebbles'); * // => 7 */ function size(collection) { if (collection == null) { return 0; } if (isArrayLike(collection)) { return isString(collection) ? stringSize(collection) : collection.length; } var tag = getTag(collection); if (tag == mapTag || tag == setTag) { return collection.size; } return baseKeys(collection).length; } /** * Checks if `predicate` returns truthy for **any** element of `collection`. * Iteration is stopped once `predicate` returns truthy. The predicate is * invoked with three arguments: (value, index|key, collection). * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {boolean} Returns `true` if any element passes the predicate check, * else `false`. * @example * * _.some([null, 0, 'yes', false], Boolean); * // => true * * var users = [ * { 'user': 'barney', 'active': true }, * { 'user': 'fred', 'active': false } * ]; * * // The `_.matches` iteratee shorthand. * _.some(users, { 'user': 'barney', 'active': false }); * // => false * * // The `_.matchesProperty` iteratee shorthand. * _.some(users, ['active', false]); * // => true * * // The `_.property` iteratee shorthand. * _.some(users, 'active'); * // => true */ function some(collection, predicate, guard) { var func = isArray(collection) ? arraySome : baseSome; if (guard && isIterateeCall(collection, predicate, guard)) { predicate = undefined; } return func(collection, getIteratee(predicate, 3)); } /** * Creates an array of elements, sorted in ascending order by the results of * running each element in a collection thru each iteratee. This method * performs a stable sort, that is, it preserves the original sort order of * equal elements. The iteratees are invoked with one argument: (value). * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {...(Function|Function[])} [iteratees=[_.identity]] * The iteratees to sort by. * @returns {Array} Returns the new sorted array. * @example * * var users = [ * { 'user': 'fred', 'age': 48 }, * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 }, * { 'user': 'barney', 'age': 34 } * ]; * * _.sortBy(users, [function(o) { return o.user; }]); * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] * * _.sortBy(users, ['user', 'age']); * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]] */ var sortBy = baseRest(function(collection, iteratees) { if (collection == null) { return []; } var length = iteratees.length; if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) { iteratees = []; } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) { iteratees = [iteratees[0]]; } return baseOrderBy(collection, baseFlatten(iteratees, 1), []); }); /*------------------------------------------------------------------------*/ /** * Gets the timestamp of the number of milliseconds that have elapsed since * the Unix epoch (1 January 1970 00:00:00 UTC). * * @static * @memberOf _ * @since 2.4.0 * @category Date * @returns {number} Returns the timestamp. * @example * * _.defer(function(stamp) { * console.log(_.now() - stamp); * }, _.now()); * // => Logs the number of milliseconds it took for the deferred invocation. */ var now = ctxNow || function() { return root.Date.now(); }; /*------------------------------------------------------------------------*/ /** * The opposite of `_.before`; this method creates a function that invokes * `func` once it's called `n` or more times. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {number} n The number of calls before `func` is invoked. * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * var saves = ['profile', 'settings']; * * var done = _.after(saves.length, function() { * console.log('done saving!'); * }); * * _.forEach(saves, function(type) { * asyncSave({ 'type': type, 'complete': done }); * }); * // => Logs 'done saving!' after the two async saves have completed. */ function after(n, func) { if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } n = toInteger(n); return function() { if (--n < 1) { return func.apply(this, arguments); } }; } /** * Creates a function that invokes `func`, with up to `n` arguments, * ignoring any additional arguments. * * @static * @memberOf _ * @since 3.0.0 * @category Function * @param {Function} func The function to cap arguments for. * @param {number} [n=func.length] The arity cap. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Function} Returns the new capped function. * @example * * _.map(['6', '8', '10'], _.ary(parseInt, 1)); * // => [6, 8, 10] */ function ary(func, n, guard) { n = guard ? undefined : n; n = (func && n == null) ? func.length : n; return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n); } /** * Creates a function that invokes `func`, with the `this` binding and arguments * of the created function, while it's called less than `n` times. Subsequent * calls to the created function return the result of the last `func` invocation. * * @static * @memberOf _ * @since 3.0.0 * @category Function * @param {number} n The number of calls at which `func` is no longer invoked. * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * jQuery(element).on('click', _.before(5, addContactToList)); * // => Allows adding up to 4 contacts to the list. */ function before(n, func) { var result; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } n = toInteger(n); return function() { if (--n > 0) { result = func.apply(this, arguments); } if (n <= 1) { func = undefined; } return result; }; } /** * Creates a function that invokes `func` with the `this` binding of `thisArg` * and `partials` prepended to the arguments it receives. * * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, * may be used as a placeholder for partially applied arguments. * * **Note:** Unlike native `Function#bind`, this method doesn't set the "length" * property of bound functions. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to bind. * @param {*} thisArg The `this` binding of `func`. * @param {...*} [partials] The arguments to be partially applied. * @returns {Function} Returns the new bound function. * @example * * function greet(greeting, punctuation) { * return greeting + ' ' + this.user + punctuation; * } * * var object = { 'user': 'fred' }; * * var bound = _.bind(greet, object, 'hi'); * bound('!'); * // => 'hi fred!' * * // Bound with placeholders. * var bound = _.bind(greet, object, _, '!'); * bound('hi'); * // => 'hi fred!' */ var bind = baseRest(function(func, thisArg, partials) { var bitmask = WRAP_BIND_FLAG; if (partials.length) { var holders = replaceHolders(partials, getHolder(bind)); bitmask |= WRAP_PARTIAL_FLAG; } return createWrap(func, bitmask, thisArg, partials, holders); }); /** * Creates a function that invokes the method at `object[key]` with `partials` * prepended to the arguments it receives. * * This method differs from `_.bind` by allowing bound functions to reference * methods that may be redefined or don't yet exist. See * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) * for more details. * * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * * @static * @memberOf _ * @since 0.10.0 * @category Function * @param {Object} object The object to invoke the method on. * @param {string} key The key of the method. * @param {...*} [partials] The arguments to be partially applied. * @returns {Function} Returns the new bound function. * @example * * var object = { * 'user': 'fred', * 'greet': function(greeting, punctuation) { * return greeting + ' ' + this.user + punctuation; * } * }; * * var bound = _.bindKey(object, 'greet', 'hi'); * bound('!'); * // => 'hi fred!' * * object.greet = function(greeting, punctuation) { * return greeting + 'ya ' + this.user + punctuation; * }; * * bound('!'); * // => 'hiya fred!' * * // Bound with placeholders. * var bound = _.bindKey(object, 'greet', _, '!'); * bound('hi'); * // => 'hiya fred!' */ var bindKey = baseRest(function(object, key, partials) { var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG; if (partials.length) { var holders = replaceHolders(partials, getHolder(bindKey)); bitmask |= WRAP_PARTIAL_FLAG; } return createWrap(key, bitmask, object, partials, holders); }); /** * Creates a function that accepts arguments of `func` and either invokes * `func` returning its result, if at least `arity` number of arguments have * been provided, or returns a function that accepts the remaining `func` * arguments, and so on. The arity of `func` may be specified if `func.length` * is not sufficient. * * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, * may be used as a placeholder for provided arguments. * * **Note:** This method doesn't set the "length" property of curried functions. * * @static * @memberOf _ * @since 2.0.0 * @category Function * @param {Function} func The function to curry. * @param {number} [arity=func.length] The arity of `func`. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Function} Returns the new curried function. * @example * * var abc = function(a, b, c) { * return [a, b, c]; * }; * * var curried = _.curry(abc); * * curried(1)(2)(3); * // => [1, 2, 3] * * curried(1, 2)(3); * // => [1, 2, 3] * * curried(1, 2, 3); * // => [1, 2, 3] * * // Curried with placeholders. * curried(1)(_, 3)(2); * // => [1, 2, 3] */ function curry(func, arity, guard) { arity = guard ? undefined : arity; var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity); result.placeholder = curry.placeholder; return result; } /** * This method is like `_.curry` except that arguments are applied to `func` * in the manner of `_.partialRight` instead of `_.partial`. * * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for provided arguments. * * **Note:** This method doesn't set the "length" property of curried functions. * * @static * @memberOf _ * @since 3.0.0 * @category Function * @param {Function} func The function to curry. * @param {number} [arity=func.length] The arity of `func`. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Function} Returns the new curried function. * @example * * var abc = function(a, b, c) { * return [a, b, c]; * }; * * var curried = _.curryRight(abc); * * curried(3)(2)(1); * // => [1, 2, 3] * * curried(2, 3)(1); * // => [1, 2, 3] * * curried(1, 2, 3); * // => [1, 2, 3] * * // Curried with placeholders. * curried(3)(1, _)(2); * // => [1, 2, 3] */ function curryRight(func, arity, guard) { arity = guard ? undefined : arity; var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity); result.placeholder = curryRight.placeholder; return result; } /** * Creates a debounced function that delays invoking `func` until after `wait` * milliseconds have elapsed since the last time the debounced function was * invoked. The debounced function comes with a `cancel` method to cancel * delayed `func` invocations and a `flush` method to immediately invoke them. * Provide `options` to indicate whether `func` should be invoked on the * leading and/or trailing edge of the `wait` timeout. The `func` is invoked * with the last arguments provided to the debounced function. Subsequent * calls to the debounced function return the result of the last `func` * invocation. * * **Note:** If `leading` and `trailing` options are `true`, `func` is * invoked on the trailing edge of the timeout only if the debounced function * is invoked more than once during the `wait` timeout. * * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred * until to the next tick, similar to `setTimeout` with a timeout of `0`. * * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) * for details over the differences between `_.debounce` and `_.throttle`. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to debounce. * @param {number} [wait=0] The number of milliseconds to delay. * @param {Object} [options={}] The options object. * @param {boolean} [options.leading=false] * Specify invoking on the leading edge of the timeout. * @param {number} [options.maxWait] * The maximum time `func` is allowed to be delayed before it's invoked. * @param {boolean} [options.trailing=true] * Specify invoking on the trailing edge of the timeout. * @returns {Function} Returns the new debounced function. * @example * * // Avoid costly calculations while the window size is in flux. * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); * * // Invoke `sendMail` when clicked, debouncing subsequent calls. * jQuery(element).on('click', _.debounce(sendMail, 300, { * 'leading': true, * 'trailing': false * })); * * // Ensure `batchLog` is invoked once after 1 second of debounced calls. * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); * var source = new EventSource('/stream'); * jQuery(source).on('message', debounced); * * // Cancel the trailing debounced invocation. * jQuery(window).on('popstate', debounced.cancel); */ function debounce(func, wait, options) { var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } wait = toNumber(wait) || 0; if (isObject(options)) { leading = !!options.leading; maxing = 'maxWait' in options; maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; trailing = 'trailing' in options ? !!options.trailing : trailing; } function invokeFunc(time) { var args = lastArgs, thisArg = lastThis; lastArgs = lastThis = undefined; lastInvokeTime = time; result = func.apply(thisArg, args); return result; } function leadingEdge(time) { // Reset any `maxWait` timer. lastInvokeTime = time; // Start the timer for the trailing edge. timerId = setTimeout(timerExpired, wait); // Invoke the leading edge. return leading ? invokeFunc(time) : result; } function remainingWait(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, timeWaiting = wait - timeSinceLastCall; return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting; } function shouldInvoke(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the // trailing edge, the system time has gone backwards and we're treating // it as the trailing edge, or we've hit the `maxWait` limit. return (lastCallTime === undefined || (timeSinceLastCall >= wait) || (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); } function timerExpired() { var time = now(); if (shouldInvoke(time)) { return trailingEdge(time); } // Restart the timer. timerId = setTimeout(timerExpired, remainingWait(time)); } function trailingEdge(time) { timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been // debounced at least once. if (trailing && lastArgs) { return invokeFunc(time); } lastArgs = lastThis = undefined; return result; } function cancel() { if (timerId !== undefined) { clearTimeout(timerId); } lastInvokeTime = 0; lastArgs = lastCallTime = lastThis = timerId = undefined; } function flush() { return timerId === undefined ? result : trailingEdge(now()); } function debounced() { var time = now(), isInvoking = shouldInvoke(time); lastArgs = arguments; lastThis = this; lastCallTime = time; if (isInvoking) { if (timerId === undefined) { return leadingEdge(lastCallTime); } if (maxing) { // Handle invocations in a tight loop. timerId = setTimeout(timerExpired, wait); return invokeFunc(lastCallTime); } } if (timerId === undefined) { timerId = setTimeout(timerExpired, wait); } return result; } debounced.cancel = cancel; debounced.flush = flush; return debounced; } /** * Defers invoking the `func` until the current call stack has cleared. Any * additional arguments are provided to `func` when it's invoked. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to defer. * @param {...*} [args] The arguments to invoke `func` with. * @returns {number} Returns the timer id. * @example * * _.defer(function(text) { * console.log(text); * }, 'deferred'); * // => Logs 'deferred' after one millisecond. */ var defer = baseRest(function(func, args) { return baseDelay(func, 1, args); }); /** * Invokes `func` after `wait` milliseconds. Any additional arguments are * provided to `func` when it's invoked. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to delay. * @param {number} wait The number of milliseconds to delay invocation. * @param {...*} [args] The arguments to invoke `func` with. * @returns {number} Returns the timer id. * @example * * _.delay(function(text) { * console.log(text); * }, 1000, 'later'); * // => Logs 'later' after one second. */ var delay = baseRest(function(func, wait, args) { return baseDelay(func, toNumber(wait) || 0, args); }); /** * Creates a function that invokes `func` with arguments reversed. * * @static * @memberOf _ * @since 4.0.0 * @category Function * @param {Function} func The function to flip arguments for. * @returns {Function} Returns the new flipped function. * @example * * var flipped = _.flip(function() { * return _.toArray(arguments); * }); * * flipped('a', 'b', 'c', 'd'); * // => ['d', 'c', 'b', 'a'] */ function flip(func) { return createWrap(func, WRAP_FLIP_FLAG); } /** * Creates a function that memoizes the result of `func`. If `resolver` is * provided, it determines the cache key for storing the result based on the * arguments provided to the memoized function. By default, the first argument * provided to the memoized function is used as the map cache key. The `func` * is invoked with the `this` binding of the memoized function. * * **Note:** The cache is exposed as the `cache` property on the memoized * function. Its creation may be customized by replacing the `_.memoize.Cache` * constructor with one whose instances implement the * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) * method interface of `clear`, `delete`, `get`, `has`, and `set`. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to have its output memoized. * @param {Function} [resolver] The function to resolve the cache key. * @returns {Function} Returns the new memoized function. * @example * * var object = { 'a': 1, 'b': 2 }; * var other = { 'c': 3, 'd': 4 }; * * var values = _.memoize(_.values); * values(object); * // => [1, 2] * * values(other); * // => [3, 4] * * object.a = 2; * values(object); * // => [1, 2] * * // Modify the result cache. * values.cache.set(object, ['a', 'b']); * values(object); * // => ['a', 'b'] * * // Replace `_.memoize.Cache`. * _.memoize.Cache = WeakMap; */ function memoize(func, resolver) { if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) { throw new TypeError(FUNC_ERROR_TEXT); } var memoized = function() { var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache; if (cache.has(key)) { return cache.get(key); } var result = func.apply(this, args); memoized.cache = cache.set(key, result) || cache; return result; }; memoized.cache = new (memoize.Cache || MapCache); return memoized; } // Expose `MapCache`. memoize.Cache = MapCache; /** * Creates a function that negates the result of the predicate `func`. The * `func` predicate is invoked with the `this` binding and arguments of the * created function. * * @static * @memberOf _ * @since 3.0.0 * @category Function * @param {Function} predicate The predicate to negate. * @returns {Function} Returns the new negated function. * @example * * function isEven(n) { * return n % 2 == 0; * } * * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); * // => [1, 3, 5] */ function negate(predicate) { if (typeof predicate != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } return function() { var args = arguments; switch (args.length) { case 0: return !predicate.call(this); case 1: return !predicate.call(this, args[0]); case 2: return !predicate.call(this, args[0], args[1]); case 3: return !predicate.call(this, args[0], args[1], args[2]); } return !predicate.apply(this, args); }; } /** * Creates a function that is restricted to invoking `func` once. Repeat calls * to the function return the value of the first invocation. The `func` is * invoked with the `this` binding and arguments of the created function. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * var initialize = _.once(createApplication); * initialize(); * initialize(); * // => `createApplication` is invoked once */ function once(func) { return before(2, func); } /** * Creates a function that invokes `func` with its arguments transformed. * * @static * @since 4.0.0 * @memberOf _ * @category Function * @param {Function} func The function to wrap. * @param {...(Function|Function[])} [transforms=[_.identity]] * The argument transforms. * @returns {Function} Returns the new function. * @example * * function doubled(n) { * return n * 2; * } * * function square(n) { * return n * n; * } * * var func = _.overArgs(function(x, y) { * return [x, y]; * }, [square, doubled]); * * func(9, 3); * // => [81, 6] * * func(10, 5); * // => [100, 10] */ var overArgs = castRest(function(func, transforms) { transforms = (transforms.length == 1 && isArray(transforms[0])) ? arrayMap(transforms[0], baseUnary(getIteratee())) : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee())); var funcsLength = transforms.length; return baseRest(function(args) { var index = -1, length = nativeMin(args.length, funcsLength); while (++index < length) { args[index] = transforms[index].call(this, args[index]); } return apply(func, this, args); }); }); /** * Creates a function that invokes `func` with `partials` prepended to the * arguments it receives. This method is like `_.bind` except it does **not** * alter the `this` binding. * * The `_.partial.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * * **Note:** This method doesn't set the "length" property of partially * applied functions. * * @static * @memberOf _ * @since 0.2.0 * @category Function * @param {Function} func The function to partially apply arguments to. * @param {...*} [partials] The arguments to be partially applied. * @returns {Function} Returns the new partially applied function. * @example * * function greet(greeting, name) { * return greeting + ' ' + name; * } * * var sayHelloTo = _.partial(greet, 'hello'); * sayHelloTo('fred'); * // => 'hello fred' * * // Partially applied with placeholders. * var greetFred = _.partial(greet, _, 'fred'); * greetFred('hi'); * // => 'hi fred' */ var partial = baseRest(function(func, partials) { var holders = replaceHolders(partials, getHolder(partial)); return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders); }); /** * This method is like `_.partial` except that partially applied arguments * are appended to the arguments it receives. * * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * * **Note:** This method doesn't set the "length" property of partially * applied functions. * * @static * @memberOf _ * @since 1.0.0 * @category Function * @param {Function} func The function to partially apply arguments to. * @param {...*} [partials] The arguments to be partially applied. * @returns {Function} Returns the new partially applied function. * @example * * function greet(greeting, name) { * return greeting + ' ' + name; * } * * var greetFred = _.partialRight(greet, 'fred'); * greetFred('hi'); * // => 'hi fred' * * // Partially applied with placeholders. * var sayHelloTo = _.partialRight(greet, 'hello', _); * sayHelloTo('fred'); * // => 'hello fred' */ var partialRight = baseRest(function(func, partials) { var holders = replaceHolders(partials, getHolder(partialRight)); return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders); }); /** * Creates a function that invokes `func` with arguments arranged according * to the specified `indexes` where the argument value at the first index is * provided as the first argument, the argument value at the second index is * provided as the second argument, and so on. * * @static * @memberOf _ * @since 3.0.0 * @category Function * @param {Function} func The function to rearrange arguments for. * @param {...(number|number[])} indexes The arranged argument indexes. * @returns {Function} Returns the new function. * @example * * var rearged = _.rearg(function(a, b, c) { * return [a, b, c]; * }, [2, 0, 1]); * * rearged('b', 'c', 'a') * // => ['a', 'b', 'c'] */ var rearg = flatRest(function(func, indexes) { return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes); }); /** * Creates a function that invokes `func` with the `this` binding of the * created function and arguments from `start` and beyond provided as * an array. * * **Note:** This method is based on the * [rest parameter](https://mdn.io/rest_parameters). * * @static * @memberOf _ * @since 4.0.0 * @category Function * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @returns {Function} Returns the new function. * @example * * var say = _.rest(function(what, names) { * return what + ' ' + _.initial(names).join(', ') + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); * }); * * say('hello', 'fred', 'barney', 'pebbles'); * // => 'hello fred, barney, & pebbles' */ function rest(func, start) { if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } start = start === undefined ? start : toInteger(start); return baseRest(func, start); } /** * Creates a function that invokes `func` with the `this` binding of the * create function and an array of arguments much like * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply). * * **Note:** This method is based on the * [spread operator](https://mdn.io/spread_operator). * * @static * @memberOf _ * @since 3.2.0 * @category Function * @param {Function} func The function to spread arguments over. * @param {number} [start=0] The start position of the spread. * @returns {Function} Returns the new function. * @example * * var say = _.spread(function(who, what) { * return who + ' says ' + what; * }); * * say(['fred', 'hello']); * // => 'fred says hello' * * var numbers = Promise.all([ * Promise.resolve(40), * Promise.resolve(36) * ]); * * numbers.then(_.spread(function(x, y) { * return x + y; * })); * // => a Promise of 76 */ function spread(func, start) { if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } start = start == null ? 0 : nativeMax(toInteger(start), 0); return baseRest(function(args) { var array = args[start], otherArgs = castSlice(args, 0, start); if (array) { arrayPush(otherArgs, array); } return apply(func, this, otherArgs); }); } /** * Creates a throttled function that only invokes `func` at most once per * every `wait` milliseconds. The throttled function comes with a `cancel` * method to cancel delayed `func` invocations and a `flush` method to * immediately invoke them. Provide `options` to indicate whether `func` * should be invoked on the leading and/or trailing edge of the `wait` * timeout. The `func` is invoked with the last arguments provided to the * throttled function. Subsequent calls to the throttled function return the * result of the last `func` invocation. * * **Note:** If `leading` and `trailing` options are `true`, `func` is * invoked on the trailing edge of the timeout only if the throttled function * is invoked more than once during the `wait` timeout. * * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred * until to the next tick, similar to `setTimeout` with a timeout of `0`. * * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) * for details over the differences between `_.throttle` and `_.debounce`. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to throttle. * @param {number} [wait=0] The number of milliseconds to throttle invocations to. * @param {Object} [options={}] The options object. * @param {boolean} [options.leading=true] * Specify invoking on the leading edge of the timeout. * @param {boolean} [options.trailing=true] * Specify invoking on the trailing edge of the timeout. * @returns {Function} Returns the new throttled function. * @example * * // Avoid excessively updating the position while scrolling. * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); * * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); * jQuery(element).on('click', throttled); * * // Cancel the trailing throttled invocation. * jQuery(window).on('popstate', throttled.cancel); */ function throttle(func, wait, options) { var leading = true, trailing = true; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } if (isObject(options)) { leading = 'leading' in options ? !!options.leading : leading; trailing = 'trailing' in options ? !!options.trailing : trailing; } return debounce(func, wait, { 'leading': leading, 'maxWait': wait, 'trailing': trailing }); } /** * Creates a function that accepts up to one argument, ignoring any * additional arguments. * * @static * @memberOf _ * @since 4.0.0 * @category Function * @param {Function} func The function to cap arguments for. * @returns {Function} Returns the new capped function. * @example * * _.map(['6', '8', '10'], _.unary(parseInt)); * // => [6, 8, 10] */ function unary(func) { return ary(func, 1); } /** * Creates a function that provides `value` to `wrapper` as its first * argument. Any additional arguments provided to the function are appended * to those provided to the `wrapper`. The wrapper is invoked with the `this` * binding of the created function. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {*} value The value to wrap. * @param {Function} [wrapper=identity] The wrapper function. * @returns {Function} Returns the new function. * @example * * var p = _.wrap(_.escape, function(func, text) { * return '

' + func(text) + '

'; * }); * * p('fred, barney, & pebbles'); * // => '

fred, barney, & pebbles

' */ function wrap(value, wrapper) { return partial(castFunction(wrapper), value); } /*------------------------------------------------------------------------*/ /** * Casts `value` as an array if it's not one. * * @static * @memberOf _ * @since 4.4.0 * @category Lang * @param {*} value The value to inspect. * @returns {Array} Returns the cast array. * @example * * _.castArray(1); * // => [1] * * _.castArray({ 'a': 1 }); * // => [{ 'a': 1 }] * * _.castArray('abc'); * // => ['abc'] * * _.castArray(null); * // => [null] * * _.castArray(undefined); * // => [undefined] * * _.castArray(); * // => [] * * var array = [1, 2, 3]; * console.log(_.castArray(array) === array); * // => true */ function castArray() { if (!arguments.length) { return []; } var value = arguments[0]; return isArray(value) ? value : [value]; } /** * Creates a shallow clone of `value`. * * **Note:** This method is loosely based on the * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm) * and supports cloning arrays, array buffers, booleans, date objects, maps, * numbers, `Object` objects, regexes, sets, strings, symbols, and typed * arrays. The own enumerable properties of `arguments` objects are cloned * as plain objects. An empty object is returned for uncloneable values such * as error objects, functions, DOM nodes, and WeakMaps. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to clone. * @returns {*} Returns the cloned value. * @see _.cloneDeep * @example * * var objects = [{ 'a': 1 }, { 'b': 2 }]; * * var shallow = _.clone(objects); * console.log(shallow[0] === objects[0]); * // => true */ function clone(value) { return baseClone(value, CLONE_SYMBOLS_FLAG); } /** * This method is like `_.clone` except that it accepts `customizer` which * is invoked to produce the cloned value. If `customizer` returns `undefined`, * cloning is handled by the method instead. The `customizer` is invoked with * up to four arguments; (value [, index|key, object, stack]). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to clone. * @param {Function} [customizer] The function to customize cloning. * @returns {*} Returns the cloned value. * @see _.cloneDeepWith * @example * * function customizer(value) { * if (_.isElement(value)) { * return value.cloneNode(false); * } * } * * var el = _.cloneWith(document.body, customizer); * * console.log(el === document.body); * // => false * console.log(el.nodeName); * // => 'BODY' * console.log(el.childNodes.length); * // => 0 */ function cloneWith(value, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined; return baseClone(value, CLONE_SYMBOLS_FLAG, customizer); } /** * This method is like `_.clone` except that it recursively clones `value`. * * @static * @memberOf _ * @since 1.0.0 * @category Lang * @param {*} value The value to recursively clone. * @returns {*} Returns the deep cloned value. * @see _.clone * @example * * var objects = [{ 'a': 1 }, { 'b': 2 }]; * * var deep = _.cloneDeep(objects); * console.log(deep[0] === objects[0]); * // => false */ function cloneDeep(value) { return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG); } /** * This method is like `_.cloneWith` except that it recursively clones `value`. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to recursively clone. * @param {Function} [customizer] The function to customize cloning. * @returns {*} Returns the deep cloned value. * @see _.cloneWith * @example * * function customizer(value) { * if (_.isElement(value)) { * return value.cloneNode(true); * } * } * * var el = _.cloneDeepWith(document.body, customizer); * * console.log(el === document.body); * // => false * console.log(el.nodeName); * // => 'BODY' * console.log(el.childNodes.length); * // => 20 */ function cloneDeepWith(value, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined; return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer); } /** * Checks if `object` conforms to `source` by invoking the predicate * properties of `source` with the corresponding property values of `object`. * * **Note:** This method is equivalent to `_.conforms` when `source` is * partially applied. * * @static * @memberOf _ * @since 4.14.0 * @category Lang * @param {Object} object The object to inspect. * @param {Object} source The object of property predicates to conform to. * @returns {boolean} Returns `true` if `object` conforms, else `false`. * @example * * var object = { 'a': 1, 'b': 2 }; * * _.conformsTo(object, { 'b': function(n) { return n > 1; } }); * // => true * * _.conformsTo(object, { 'b': function(n) { return n > 2; } }); * // => false */ function conformsTo(object, source) { return source == null || baseConformsTo(object, source, keys(source)); } /** * Performs a * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * comparison between two values to determine if they are equivalent. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * var object = { 'a': 1 }; * var other = { 'a': 1 }; * * _.eq(object, object); * // => true * * _.eq(object, other); * // => false * * _.eq('a', 'a'); * // => true * * _.eq('a', Object('a')); * // => false * * _.eq(NaN, NaN); * // => true */ function eq(value, other) { return value === other || (value !== value && other !== other); } /** * Checks if `value` is greater than `other`. * * @static * @memberOf _ * @since 3.9.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if `value` is greater than `other`, * else `false`. * @see _.lt * @example * * _.gt(3, 1); * // => true * * _.gt(3, 3); * // => false * * _.gt(1, 3); * // => false */ var gt = createRelationalOperation(baseGt); /** * Checks if `value` is greater than or equal to `other`. * * @static * @memberOf _ * @since 3.9.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if `value` is greater than or equal to * `other`, else `false`. * @see _.lte * @example * * _.gte(3, 1); * // => true * * _.gte(3, 3); * // => true * * _.gte(1, 3); * // => false */ var gte = createRelationalOperation(function(value, other) { return value >= other; }); /** * Checks if `value` is likely an `arguments` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an `arguments` object, * else `false`. * @example * * _.isArguments(function() { return arguments; }()); * // => true * * _.isArguments([1, 2, 3]); * // => false */ var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) { return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); }; /** * Checks if `value` is classified as an `Array` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array, else `false`. * @example * * _.isArray([1, 2, 3]); * // => true * * _.isArray(document.body.children); * // => false * * _.isArray('abc'); * // => false * * _.isArray(_.noop); * // => false */ var isArray = Array.isArray; /** * Checks if `value` is classified as an `ArrayBuffer` object. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. * @example * * _.isArrayBuffer(new ArrayBuffer(2)); * // => true * * _.isArrayBuffer(new Array(2)); * // => false */ var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer; /** * Checks if `value` is array-like. A value is considered array-like if it's * not a function and has a `value.length` that's an integer greater than or * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is array-like, else `false`. * @example * * _.isArrayLike([1, 2, 3]); * // => true * * _.isArrayLike(document.body.children); * // => true * * _.isArrayLike('abc'); * // => true * * _.isArrayLike(_.noop); * // => false */ function isArrayLike(value) { return value != null && isLength(value.length) && !isFunction(value); } /** * This method is like `_.isArrayLike` except that it also checks if `value` * is an object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array-like object, * else `false`. * @example * * _.isArrayLikeObject([1, 2, 3]); * // => true * * _.isArrayLikeObject(document.body.children); * // => true * * _.isArrayLikeObject('abc'); * // => false * * _.isArrayLikeObject(_.noop); * // => false */ function isArrayLikeObject(value) { return isObjectLike(value) && isArrayLike(value); } /** * Checks if `value` is classified as a boolean primitive or object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a boolean, else `false`. * @example * * _.isBoolean(false); * // => true * * _.isBoolean(null); * // => false */ function isBoolean(value) { return value === true || value === false || (isObjectLike(value) && baseGetTag(value) == boolTag); } /** * Checks if `value` is a buffer. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. * @example * * _.isBuffer(new Buffer(2)); * // => true * * _.isBuffer(new Uint8Array(2)); * // => false */ var isBuffer = nativeIsBuffer || stubFalse; /** * Checks if `value` is classified as a `Date` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a date object, else `false`. * @example * * _.isDate(new Date); * // => true * * _.isDate('Mon April 23 2012'); * // => false */ var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate; /** * Checks if `value` is likely a DOM element. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. * @example * * _.isElement(document.body); * // => true * * _.isElement(''); * // => false */ function isElement(value) { return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value); } /** * Checks if `value` is an empty object, collection, map, or set. * * Objects are considered empty if they have no own enumerable string keyed * properties. * * Array-like values such as `arguments` objects, arrays, buffers, strings, or * jQuery-like collections are considered empty if they have a `length` of `0`. * Similarly, maps and sets are considered empty if they have a `size` of `0`. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is empty, else `false`. * @example * * _.isEmpty(null); * // => true * * _.isEmpty(true); * // => true * * _.isEmpty(1); * // => true * * _.isEmpty([1, 2, 3]); * // => false * * _.isEmpty({ 'a': 1 }); * // => false */ function isEmpty(value) { if (value == null) { return true; } if (isArrayLike(value) && (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' || isBuffer(value) || isTypedArray(value) || isArguments(value))) { return !value.length; } var tag = getTag(value); if (tag == mapTag || tag == setTag) { return !value.size; } if (isPrototype(value)) { return !baseKeys(value).length; } for (var key in value) { if (hasOwnProperty.call(value, key)) { return false; } } return true; } /** * Performs a deep comparison between two values to determine if they are * equivalent. * * **Note:** This method supports comparing arrays, array buffers, booleans, * date objects, error objects, maps, numbers, `Object` objects, regexes, * sets, strings, symbols, and typed arrays. `Object` objects are compared * by their own, not inherited, enumerable properties. Functions and DOM * nodes are compared by strict equality, i.e. `===`. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * var object = { 'a': 1 }; * var other = { 'a': 1 }; * * _.isEqual(object, other); * // => true * * object === other; * // => false */ function isEqual(value, other) { return baseIsEqual(value, other); } /** * This method is like `_.isEqual` except that it accepts `customizer` which * is invoked to compare values. If `customizer` returns `undefined`, comparisons * are handled by the method instead. The `customizer` is invoked with up to * six arguments: (objValue, othValue [, index|key, object, other, stack]). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @param {Function} [customizer] The function to customize comparisons. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * function isGreeting(value) { * return /^h(?:i|ello)$/.test(value); * } * * function customizer(objValue, othValue) { * if (isGreeting(objValue) && isGreeting(othValue)) { * return true; * } * } * * var array = ['hello', 'goodbye']; * var other = ['hi', 'goodbye']; * * _.isEqualWith(array, other, customizer); * // => true */ function isEqualWith(value, other, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined; var result = customizer ? customizer(value, other) : undefined; return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result; } /** * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, * `SyntaxError`, `TypeError`, or `URIError` object. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an error object, else `false`. * @example * * _.isError(new Error); * // => true * * _.isError(Error); * // => false */ function isError(value) { if (!isObjectLike(value)) { return false; } var tag = baseGetTag(value); return tag == errorTag || tag == domExcTag || (typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value)); } /** * Checks if `value` is a finite primitive number. * * **Note:** This method is based on * [`Number.isFinite`](https://mdn.io/Number/isFinite). * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. * @example * * _.isFinite(3); * // => true * * _.isFinite(Number.MIN_VALUE); * // => true * * _.isFinite(Infinity); * // => false * * _.isFinite('3'); * // => false */ function isFinite(value) { return typeof value == 'number' && nativeIsFinite(value); } /** * Checks if `value` is classified as a `Function` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a function, else `false`. * @example * * _.isFunction(_); * // => true * * _.isFunction(/abc/); * // => false */ function isFunction(value) { if (!isObject(value)) { return false; } // The use of `Object#toString` avoids issues with the `typeof` operator // in Safari 9 which returns 'object' for typed arrays and other constructors. var tag = baseGetTag(value); return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; } /** * Checks if `value` is an integer. * * **Note:** This method is based on * [`Number.isInteger`](https://mdn.io/Number/isInteger). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an integer, else `false`. * @example * * _.isInteger(3); * // => true * * _.isInteger(Number.MIN_VALUE); * // => false * * _.isInteger(Infinity); * // => false * * _.isInteger('3'); * // => false */ function isInteger(value) { return typeof value == 'number' && value == toInteger(value); } /** * Checks if `value` is a valid array-like length. * * **Note:** This method is loosely based on * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. * @example * * _.isLength(3); * // => true * * _.isLength(Number.MIN_VALUE); * // => false * * _.isLength(Infinity); * // => false * * _.isLength('3'); * // => false */ function isLength(value) { return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; } /** * Checks if `value` is the * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an object, else `false`. * @example * * _.isObject({}); * // => true * * _.isObject([1, 2, 3]); * // => true * * _.isObject(_.noop); * // => true * * _.isObject(null); * // => false */ function isObject(value) { var type = typeof value; return value != null && (type == 'object' || type == 'function'); } /** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. * @example * * _.isObjectLike({}); * // => true * * _.isObjectLike([1, 2, 3]); * // => true * * _.isObjectLike(_.noop); * // => false * * _.isObjectLike(null); * // => false */ function isObjectLike(value) { return value != null && typeof value == 'object'; } /** * Checks if `value` is classified as a `Map` object. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a map, else `false`. * @example * * _.isMap(new Map); * // => true * * _.isMap(new WeakMap); * // => false */ var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap; /** * Performs a partial deep comparison between `object` and `source` to * determine if `object` contains equivalent property values. * * **Note:** This method is equivalent to `_.matches` when `source` is * partially applied. * * Partial comparisons will match empty array and empty object `source` * values against any array or object value, respectively. See `_.isEqual` * for a list of supported value comparisons. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {Object} object The object to inspect. * @param {Object} source The object of property values to match. * @returns {boolean} Returns `true` if `object` is a match, else `false`. * @example * * var object = { 'a': 1, 'b': 2 }; * * _.isMatch(object, { 'b': 2 }); * // => true * * _.isMatch(object, { 'b': 1 }); * // => false */ function isMatch(object, source) { return object === source || baseIsMatch(object, source, getMatchData(source)); } /** * This method is like `_.isMatch` except that it accepts `customizer` which * is invoked to compare values. If `customizer` returns `undefined`, comparisons * are handled by the method instead. The `customizer` is invoked with five * arguments: (objValue, srcValue, index|key, object, source). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {Object} object The object to inspect. * @param {Object} source The object of property values to match. * @param {Function} [customizer] The function to customize comparisons. * @returns {boolean} Returns `true` if `object` is a match, else `false`. * @example * * function isGreeting(value) { * return /^h(?:i|ello)$/.test(value); * } * * function customizer(objValue, srcValue) { * if (isGreeting(objValue) && isGreeting(srcValue)) { * return true; * } * } * * var object = { 'greeting': 'hello' }; * var source = { 'greeting': 'hi' }; * * _.isMatchWith(object, source, customizer); * // => true */ function isMatchWith(object, source, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined; return baseIsMatch(object, source, getMatchData(source), customizer); } /** * Checks if `value` is `NaN`. * * **Note:** This method is based on * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for * `undefined` and other non-number values. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. * @example * * _.isNaN(NaN); * // => true * * _.isNaN(new Number(NaN)); * // => true * * isNaN(undefined); * // => true * * _.isNaN(undefined); * // => false */ function isNaN(value) { // An `NaN` primitive is the only value that is not equal to itself. // Perform the `toStringTag` check first to avoid errors with some // ActiveX objects in IE. return isNumber(value) && value != +value; } /** * Checks if `value` is a pristine native function. * * **Note:** This method can't reliably detect native functions in the presence * of the core-js package because core-js circumvents this kind of detection. * Despite multiple requests, the core-js maintainer has made it clear: any * attempt to fix the detection will be obstructed. As a result, we're left * with little choice but to throw an error. Unfortunately, this also affects * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill), * which rely on core-js. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a native function, * else `false`. * @example * * _.isNative(Array.prototype.push); * // => true * * _.isNative(_); * // => false */ function isNative(value) { if (isMaskable(value)) { throw new Error(CORE_ERROR_TEXT); } return baseIsNative(value); } /** * Checks if `value` is `null`. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `null`, else `false`. * @example * * _.isNull(null); * // => true * * _.isNull(void 0); * // => false */ function isNull(value) { return value === null; } /** * Checks if `value` is `null` or `undefined`. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is nullish, else `false`. * @example * * _.isNil(null); * // => true * * _.isNil(void 0); * // => true * * _.isNil(NaN); * // => false */ function isNil(value) { return value == null; } /** * Checks if `value` is classified as a `Number` primitive or object. * * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are * classified as numbers, use the `_.isFinite` method. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a number, else `false`. * @example * * _.isNumber(3); * // => true * * _.isNumber(Number.MIN_VALUE); * // => true * * _.isNumber(Infinity); * // => true * * _.isNumber('3'); * // => false */ function isNumber(value) { return typeof value == 'number' || (isObjectLike(value) && baseGetTag(value) == numberTag); } /** * Checks if `value` is a plain object, that is, an object created by the * `Object` constructor or one with a `[[Prototype]]` of `null`. * * @static * @memberOf _ * @since 0.8.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. * @example * * function Foo() { * this.a = 1; * } * * _.isPlainObject(new Foo); * // => false * * _.isPlainObject([1, 2, 3]); * // => false * * _.isPlainObject({ 'x': 0, 'y': 0 }); * // => true * * _.isPlainObject(Object.create(null)); * // => true */ function isPlainObject(value) { if (!isObjectLike(value) || baseGetTag(value) != objectTag) { return false; } var proto = getPrototype(value); if (proto === null) { return true; } var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; return typeof Ctor == 'function' && Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString; } /** * Checks if `value` is classified as a `RegExp` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. * @example * * _.isRegExp(/abc/); * // => true * * _.isRegExp('/abc/'); * // => false */ var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp; /** * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754 * double precision number which isn't the result of a rounded unsafe integer. * * **Note:** This method is based on * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`. * @example * * _.isSafeInteger(3); * // => true * * _.isSafeInteger(Number.MIN_VALUE); * // => false * * _.isSafeInteger(Infinity); * // => false * * _.isSafeInteger('3'); * // => false */ function isSafeInteger(value) { return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER; } /** * Checks if `value` is classified as a `Set` object. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a set, else `false`. * @example * * _.isSet(new Set); * // => true * * _.isSet(new WeakSet); * // => false */ var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet; /** * Checks if `value` is classified as a `String` primitive or object. * * @static * @since 0.1.0 * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a string, else `false`. * @example * * _.isString('abc'); * // => true * * _.isString(1); * // => false */ function isString(value) { return typeof value == 'string' || (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag); } /** * Checks if `value` is classified as a `Symbol` primitive or object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. * @example * * _.isSymbol(Symbol.iterator); * // => true * * _.isSymbol('abc'); * // => false */ function isSymbol(value) { return typeof value == 'symbol' || (isObjectLike(value) && baseGetTag(value) == symbolTag); } /** * Checks if `value` is classified as a typed array. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. * @example * * _.isTypedArray(new Uint8Array); * // => true * * _.isTypedArray([]); * // => false */ var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; /** * Checks if `value` is `undefined`. * * @static * @since 0.1.0 * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. * @example * * _.isUndefined(void 0); * // => true * * _.isUndefined(null); * // => false */ function isUndefined(value) { return value === undefined; } /** * Checks if `value` is classified as a `WeakMap` object. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a weak map, else `false`. * @example * * _.isWeakMap(new WeakMap); * // => true * * _.isWeakMap(new Map); * // => false */ function isWeakMap(value) { return isObjectLike(value) && getTag(value) == weakMapTag; } /** * Checks if `value` is classified as a `WeakSet` object. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a weak set, else `false`. * @example * * _.isWeakSet(new WeakSet); * // => true * * _.isWeakSet(new Set); * // => false */ function isWeakSet(value) { return isObjectLike(value) && baseGetTag(value) == weakSetTag; } /** * Checks if `value` is less than `other`. * * @static * @memberOf _ * @since 3.9.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if `value` is less than `other`, * else `false`. * @see _.gt * @example * * _.lt(1, 3); * // => true * * _.lt(3, 3); * // => false * * _.lt(3, 1); * // => false */ var lt = createRelationalOperation(baseLt); /** * Checks if `value` is less than or equal to `other`. * * @static * @memberOf _ * @since 3.9.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if `value` is less than or equal to * `other`, else `false`. * @see _.gte * @example * * _.lte(1, 3); * // => true * * _.lte(3, 3); * // => true * * _.lte(3, 1); * // => false */ var lte = createRelationalOperation(function(value, other) { return value <= other; }); /** * Converts `value` to an array. * * @static * @since 0.1.0 * @memberOf _ * @category Lang * @param {*} value The value to convert. * @returns {Array} Returns the converted array. * @example * * _.toArray({ 'a': 1, 'b': 2 }); * // => [1, 2] * * _.toArray('abc'); * // => ['a', 'b', 'c'] * * _.toArray(1); * // => [] * * _.toArray(null); * // => [] */ function toArray(value) { if (!value) { return []; } if (isArrayLike(value)) { return isString(value) ? stringToArray(value) : copyArray(value); } if (symIterator && value[symIterator]) { return iteratorToArray(value[symIterator]()); } var tag = getTag(value), func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values); return func(value); } /** * Converts `value` to a finite number. * * @static * @memberOf _ * @since 4.12.0 * @category Lang * @param {*} value The value to convert. * @returns {number} Returns the converted number. * @example * * _.toFinite(3.2); * // => 3.2 * * _.toFinite(Number.MIN_VALUE); * // => 5e-324 * * _.toFinite(Infinity); * // => 1.7976931348623157e+308 * * _.toFinite('3.2'); * // => 3.2 */ function toFinite(value) { if (!value) { return value === 0 ? value : 0; } value = toNumber(value); if (value === INFINITY || value === -INFINITY) { var sign = (value < 0 ? -1 : 1); return sign * MAX_INTEGER; } return value === value ? value : 0; } /** * Converts `value` to an integer. * * **Note:** This method is loosely based on * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to convert. * @returns {number} Returns the converted integer. * @example * * _.toInteger(3.2); * // => 3 * * _.toInteger(Number.MIN_VALUE); * // => 0 * * _.toInteger(Infinity); * // => 1.7976931348623157e+308 * * _.toInteger('3.2'); * // => 3 */ function toInteger(value) { var result = toFinite(value), remainder = result % 1; return result === result ? (remainder ? result - remainder : result) : 0; } /** * Converts `value` to an integer suitable for use as the length of an * array-like object. * * **Note:** This method is based on * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to convert. * @returns {number} Returns the converted integer. * @example * * _.toLength(3.2); * // => 3 * * _.toLength(Number.MIN_VALUE); * // => 0 * * _.toLength(Infinity); * // => 4294967295 * * _.toLength('3.2'); * // => 3 */ function toLength(value) { return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0; } /** * Converts `value` to a number. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to process. * @returns {number} Returns the number. * @example * * _.toNumber(3.2); * // => 3.2 * * _.toNumber(Number.MIN_VALUE); * // => 5e-324 * * _.toNumber(Infinity); * // => Infinity * * _.toNumber('3.2'); * // => 3.2 */ function toNumber(value) { if (typeof value == 'number') { return value; } if (isSymbol(value)) { return NAN; } if (isObject(value)) { var other = typeof value.valueOf == 'function' ? value.valueOf() : value; value = isObject(other) ? (other + '') : other; } if (typeof value != 'string') { return value === 0 ? value : +value; } value = value.replace(reTrim, ''); var isBinary = reIsBinary.test(value); return (isBinary || reIsOctal.test(value)) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : (reIsBadHex.test(value) ? NAN : +value); } /** * Converts `value` to a plain object flattening inherited enumerable string * keyed properties of `value` to own properties of the plain object. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {*} value The value to convert. * @returns {Object} Returns the converted plain object. * @example * * function Foo() { * this.b = 2; * } * * Foo.prototype.c = 3; * * _.assign({ 'a': 1 }, new Foo); * // => { 'a': 1, 'b': 2 } * * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); * // => { 'a': 1, 'b': 2, 'c': 3 } */ function toPlainObject(value) { return copyObject(value, keysIn(value)); } /** * Converts `value` to a safe integer. A safe integer can be compared and * represented correctly. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to convert. * @returns {number} Returns the converted integer. * @example * * _.toSafeInteger(3.2); * // => 3 * * _.toSafeInteger(Number.MIN_VALUE); * // => 0 * * _.toSafeInteger(Infinity); * // => 9007199254740991 * * _.toSafeInteger('3.2'); * // => 3 */ function toSafeInteger(value) { return value ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER) : (value === 0 ? value : 0); } /** * Converts `value` to a string. An empty string is returned for `null` * and `undefined` values. The sign of `-0` is preserved. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to convert. * @returns {string} Returns the converted string. * @example * * _.toString(null); * // => '' * * _.toString(-0); * // => '-0' * * _.toString([1, 2, 3]); * // => '1,2,3' */ function toString(value) { return value == null ? '' : baseToString(value); } /*------------------------------------------------------------------------*/ /** * Assigns own enumerable string keyed properties of source objects to the * destination object. Source objects are applied from left to right. * Subsequent sources overwrite property assignments of previous sources. * * **Note:** This method mutates `object` and is loosely based on * [`Object.assign`](https://mdn.io/Object/assign). * * @static * @memberOf _ * @since 0.10.0 * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @see _.assignIn * @example * * function Foo() { * this.a = 1; * } * * function Bar() { * this.c = 3; * } * * Foo.prototype.b = 2; * Bar.prototype.d = 4; * * _.assign({ 'a': 0 }, new Foo, new Bar); * // => { 'a': 1, 'c': 3 } */ var assign = createAssigner(function(object, source) { if (isPrototype(source) || isArrayLike(source)) { copyObject(source, keys(source), object); return; } for (var key in source) { if (hasOwnProperty.call(source, key)) { assignValue(object, key, source[key]); } } }); /** * This method is like `_.assign` except that it iterates over own and * inherited source properties. * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @alias extend * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @see _.assign * @example * * function Foo() { * this.a = 1; * } * * function Bar() { * this.c = 3; * } * * Foo.prototype.b = 2; * Bar.prototype.d = 4; * * _.assignIn({ 'a': 0 }, new Foo, new Bar); * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 } */ var assignIn = createAssigner(function(object, source) { copyObject(source, keysIn(source), object); }); /** * This method is like `_.assignIn` except that it accepts `customizer` * which is invoked to produce the assigned values. If `customizer` returns * `undefined`, assignment is handled by the method instead. The `customizer` * is invoked with five arguments: (objValue, srcValue, key, object, source). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @alias extendWith * @category Object * @param {Object} object The destination object. * @param {...Object} sources The source objects. * @param {Function} [customizer] The function to customize assigned values. * @returns {Object} Returns `object`. * @see _.assignWith * @example * * function customizer(objValue, srcValue) { * return _.isUndefined(objValue) ? srcValue : objValue; * } * * var defaults = _.partialRight(_.assignInWith, customizer); * * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); * // => { 'a': 1, 'b': 2 } */ var assignInWith = createAssigner(function(object, source, srcIndex, customizer) { copyObject(source, keysIn(source), object, customizer); }); /** * This method is like `_.assign` except that it accepts `customizer` * which is invoked to produce the assigned values. If `customizer` returns * `undefined`, assignment is handled by the method instead. The `customizer` * is invoked with five arguments: (objValue, srcValue, key, object, source). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The destination object. * @param {...Object} sources The source objects. * @param {Function} [customizer] The function to customize assigned values. * @returns {Object} Returns `object`. * @see _.assignInWith * @example * * function customizer(objValue, srcValue) { * return _.isUndefined(objValue) ? srcValue : objValue; * } * * var defaults = _.partialRight(_.assignWith, customizer); * * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); * // => { 'a': 1, 'b': 2 } */ var assignWith = createAssigner(function(object, source, srcIndex, customizer) { copyObject(source, keys(source), object, customizer); }); /** * Creates an array of values corresponding to `paths` of `object`. * * @static * @memberOf _ * @since 1.0.0 * @category Object * @param {Object} object The object to iterate over. * @param {...(string|string[])} [paths] The property paths to pick. * @returns {Array} Returns the picked values. * @example * * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; * * _.at(object, ['a[0].b.c', 'a[1]']); * // => [3, 4] */ var at = flatRest(baseAt); /** * Creates an object that inherits from the `prototype` object. If a * `properties` object is given, its own enumerable string keyed properties * are assigned to the created object. * * @static * @memberOf _ * @since 2.3.0 * @category Object * @param {Object} prototype The object to inherit from. * @param {Object} [properties] The properties to assign to the object. * @returns {Object} Returns the new object. * @example * * function Shape() { * this.x = 0; * this.y = 0; * } * * function Circle() { * Shape.call(this); * } * * Circle.prototype = _.create(Shape.prototype, { * 'constructor': Circle * }); * * var circle = new Circle; * circle instanceof Circle; * // => true * * circle instanceof Shape; * // => true */ function create(prototype, properties) { var result = baseCreate(prototype); return properties == null ? result : baseAssign(result, properties); } /** * Assigns own and inherited enumerable string keyed properties of source * objects to the destination object for all destination properties that * resolve to `undefined`. Source objects are applied from left to right. * Once a property is set, additional values of the same property are ignored. * * **Note:** This method mutates `object`. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @see _.defaultsDeep * @example * * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); * // => { 'a': 1, 'b': 2 } */ var defaults = baseRest(function(object, sources) { object = Object(object); var index = -1; var length = sources.length; var guard = length > 2 ? sources[2] : undefined; if (guard && isIterateeCall(sources[0], sources[1], guard)) { length = 1; } while (++index < length) { var source = sources[index]; var props = keysIn(source); var propsIndex = -1; var propsLength = props.length; while (++propsIndex < propsLength) { var key = props[propsIndex]; var value = object[key]; if (value === undefined || (eq(value, objectProto[key]) && !hasOwnProperty.call(object, key))) { object[key] = source[key]; } } } return object; }); /** * This method is like `_.defaults` except that it recursively assigns * default properties. * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 3.10.0 * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @see _.defaults * @example * * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } }); * // => { 'a': { 'b': 2, 'c': 3 } } */ var defaultsDeep = baseRest(function(args) { args.push(undefined, customDefaultsMerge); return apply(mergeWith, undefined, args); }); /** * This method is like `_.find` except that it returns the key of the first * element `predicate` returns truthy for instead of the element itself. * * @static * @memberOf _ * @since 1.1.0 * @category Object * @param {Object} object The object to inspect. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {string|undefined} Returns the key of the matched element, * else `undefined`. * @example * * var users = { * 'barney': { 'age': 36, 'active': true }, * 'fred': { 'age': 40, 'active': false }, * 'pebbles': { 'age': 1, 'active': true } * }; * * _.findKey(users, function(o) { return o.age < 40; }); * // => 'barney' (iteration order is not guaranteed) * * // The `_.matches` iteratee shorthand. * _.findKey(users, { 'age': 1, 'active': true }); * // => 'pebbles' * * // The `_.matchesProperty` iteratee shorthand. * _.findKey(users, ['active', false]); * // => 'fred' * * // The `_.property` iteratee shorthand. * _.findKey(users, 'active'); * // => 'barney' */ function findKey(object, predicate) { return baseFindKey(object, getIteratee(predicate, 3), baseForOwn); } /** * This method is like `_.findKey` except that it iterates over elements of * a collection in the opposite order. * * @static * @memberOf _ * @since 2.0.0 * @category Object * @param {Object} object The object to inspect. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {string|undefined} Returns the key of the matched element, * else `undefined`. * @example * * var users = { * 'barney': { 'age': 36, 'active': true }, * 'fred': { 'age': 40, 'active': false }, * 'pebbles': { 'age': 1, 'active': true } * }; * * _.findLastKey(users, function(o) { return o.age < 40; }); * // => returns 'pebbles' assuming `_.findKey` returns 'barney' * * // The `_.matches` iteratee shorthand. * _.findLastKey(users, { 'age': 36, 'active': true }); * // => 'barney' * * // The `_.matchesProperty` iteratee shorthand. * _.findLastKey(users, ['active', false]); * // => 'fred' * * // The `_.property` iteratee shorthand. * _.findLastKey(users, 'active'); * // => 'pebbles' */ function findLastKey(object, predicate) { return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight); } /** * Iterates over own and inherited enumerable string keyed properties of an * object and invokes `iteratee` for each property. The iteratee is invoked * with three arguments: (value, key, object). Iteratee functions may exit * iteration early by explicitly returning `false`. * * @static * @memberOf _ * @since 0.3.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Object} Returns `object`. * @see _.forInRight * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.forIn(new Foo, function(value, key) { * console.log(key); * }); * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed). */ function forIn(object, iteratee) { return object == null ? object : baseFor(object, getIteratee(iteratee, 3), keysIn); } /** * This method is like `_.forIn` except that it iterates over properties of * `object` in the opposite order. * * @static * @memberOf _ * @since 2.0.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Object} Returns `object`. * @see _.forIn * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.forInRight(new Foo, function(value, key) { * console.log(key); * }); * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'. */ function forInRight(object, iteratee) { return object == null ? object : baseForRight(object, getIteratee(iteratee, 3), keysIn); } /** * Iterates over own enumerable string keyed properties of an object and * invokes `iteratee` for each property. The iteratee is invoked with three * arguments: (value, key, object). Iteratee functions may exit iteration * early by explicitly returning `false`. * * @static * @memberOf _ * @since 0.3.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Object} Returns `object`. * @see _.forOwnRight * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.forOwn(new Foo, function(value, key) { * console.log(key); * }); * // => Logs 'a' then 'b' (iteration order is not guaranteed). */ function forOwn(object, iteratee) { return object && baseForOwn(object, getIteratee(iteratee, 3)); } /** * This method is like `_.forOwn` except that it iterates over properties of * `object` in the opposite order. * * @static * @memberOf _ * @since 2.0.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Object} Returns `object`. * @see _.forOwn * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.forOwnRight(new Foo, function(value, key) { * console.log(key); * }); * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'. */ function forOwnRight(object, iteratee) { return object && baseForOwnRight(object, getIteratee(iteratee, 3)); } /** * Creates an array of function property names from own enumerable properties * of `object`. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to inspect. * @returns {Array} Returns the function names. * @see _.functionsIn * @example * * function Foo() { * this.a = _.constant('a'); * this.b = _.constant('b'); * } * * Foo.prototype.c = _.constant('c'); * * _.functions(new Foo); * // => ['a', 'b'] */ function functions(object) { return object == null ? [] : baseFunctions(object, keys(object)); } /** * Creates an array of function property names from own and inherited * enumerable properties of `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to inspect. * @returns {Array} Returns the function names. * @see _.functions * @example * * function Foo() { * this.a = _.constant('a'); * this.b = _.constant('b'); * } * * Foo.prototype.c = _.constant('c'); * * _.functionsIn(new Foo); * // => ['a', 'b', 'c'] */ function functionsIn(object) { return object == null ? [] : baseFunctions(object, keysIn(object)); } /** * Gets the value at `path` of `object`. If the resolved value is * `undefined`, the `defaultValue` is returned in its place. * * @static * @memberOf _ * @since 3.7.0 * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path of the property to get. * @param {*} [defaultValue] The value returned for `undefined` resolved values. * @returns {*} Returns the resolved value. * @example * * var object = { 'a': [{ 'b': { 'c': 3 } }] }; * * _.get(object, 'a[0].b.c'); * // => 3 * * _.get(object, ['a', '0', 'b', 'c']); * // => 3 * * _.get(object, 'a.b.c', 'default'); * // => 'default' */ function get(object, path, defaultValue) { var result = object == null ? undefined : baseGet(object, path); return result === undefined ? defaultValue : result; } /** * Checks if `path` is a direct property of `object`. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path to check. * @returns {boolean} Returns `true` if `path` exists, else `false`. * @example * * var object = { 'a': { 'b': 2 } }; * var other = _.create({ 'a': _.create({ 'b': 2 }) }); * * _.has(object, 'a'); * // => true * * _.has(object, 'a.b'); * // => true * * _.has(object, ['a', 'b']); * // => true * * _.has(other, 'a'); * // => false */ function has(object, path) { return object != null && hasPath(object, path, baseHas); } /** * Checks if `path` is a direct or inherited property of `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path to check. * @returns {boolean} Returns `true` if `path` exists, else `false`. * @example * * var object = _.create({ 'a': _.create({ 'b': 2 }) }); * * _.hasIn(object, 'a'); * // => true * * _.hasIn(object, 'a.b'); * // => true * * _.hasIn(object, ['a', 'b']); * // => true * * _.hasIn(object, 'b'); * // => false */ function hasIn(object, path) { return object != null && hasPath(object, path, baseHasIn); } /** * Creates an object composed of the inverted keys and values of `object`. * If `object` contains duplicate values, subsequent values overwrite * property assignments of previous values. * * @static * @memberOf _ * @since 0.7.0 * @category Object * @param {Object} object The object to invert. * @returns {Object} Returns the new inverted object. * @example * * var object = { 'a': 1, 'b': 2, 'c': 1 }; * * _.invert(object); * // => { '1': 'c', '2': 'b' } */ var invert = createInverter(function(result, value, key) { if (value != null && typeof value.toString != 'function') { value = nativeObjectToString.call(value); } result[value] = key; }, constant(identity)); /** * This method is like `_.invert` except that the inverted object is generated * from the results of running each element of `object` thru `iteratee`. The * corresponding inverted value of each inverted key is an array of keys * responsible for generating the inverted value. The iteratee is invoked * with one argument: (value). * * @static * @memberOf _ * @since 4.1.0 * @category Object * @param {Object} object The object to invert. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Object} Returns the new inverted object. * @example * * var object = { 'a': 1, 'b': 2, 'c': 1 }; * * _.invertBy(object); * // => { '1': ['a', 'c'], '2': ['b'] } * * _.invertBy(object, function(value) { * return 'group' + value; * }); * // => { 'group1': ['a', 'c'], 'group2': ['b'] } */ var invertBy = createInverter(function(result, value, key) { if (value != null && typeof value.toString != 'function') { value = nativeObjectToString.call(value); } if (hasOwnProperty.call(result, value)) { result[value].push(key); } else { result[value] = [key]; } }, getIteratee); /** * Invokes the method at `path` of `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path of the method to invoke. * @param {...*} [args] The arguments to invoke the method with. * @returns {*} Returns the result of the invoked method. * @example * * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] }; * * _.invoke(object, 'a[0].b.c.slice', 1, 3); * // => [2, 3] */ var invoke = baseRest(baseInvoke); /** * Creates an array of the own enumerable property names of `object`. * * **Note:** Non-object values are coerced to objects. See the * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) * for more details. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.keys(new Foo); * // => ['a', 'b'] (iteration order is not guaranteed) * * _.keys('hi'); * // => ['0', '1'] */ function keys(object) { return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); } /** * Creates an array of the own and inherited enumerable property names of `object`. * * **Note:** Non-object values are coerced to objects. * * @static * @memberOf _ * @since 3.0.0 * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.keysIn(new Foo); * // => ['a', 'b', 'c'] (iteration order is not guaranteed) */ function keysIn(object) { return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); } /** * The opposite of `_.mapValues`; this method creates an object with the * same values as `object` and keys generated by running each own enumerable * string keyed property of `object` thru `iteratee`. The iteratee is invoked * with three arguments: (value, key, object). * * @static * @memberOf _ * @since 3.8.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Object} Returns the new mapped object. * @see _.mapValues * @example * * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { * return key + value; * }); * // => { 'a1': 1, 'b2': 2 } */ function mapKeys(object, iteratee) { var result = {}; iteratee = getIteratee(iteratee, 3); baseForOwn(object, function(value, key, object) { baseAssignValue(result, iteratee(value, key, object), value); }); return result; } /** * Creates an object with the same keys as `object` and values generated * by running each own enumerable string keyed property of `object` thru * `iteratee`. The iteratee is invoked with three arguments: * (value, key, object). * * @static * @memberOf _ * @since 2.4.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Object} Returns the new mapped object. * @see _.mapKeys * @example * * var users = { * 'fred': { 'user': 'fred', 'age': 40 }, * 'pebbles': { 'user': 'pebbles', 'age': 1 } * }; * * _.mapValues(users, function(o) { return o.age; }); * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) * * // The `_.property` iteratee shorthand. * _.mapValues(users, 'age'); * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) */ function mapValues(object, iteratee) { var result = {}; iteratee = getIteratee(iteratee, 3); baseForOwn(object, function(value, key, object) { baseAssignValue(result, key, iteratee(value, key, object)); }); return result; } /** * This method is like `_.assign` except that it recursively merges own and * inherited enumerable string keyed properties of source objects into the * destination object. Source properties that resolve to `undefined` are * skipped if a destination value exists. Array and plain object properties * are merged recursively. Other objects and value types are overridden by * assignment. Source objects are applied from left to right. Subsequent * sources overwrite property assignments of previous sources. * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 0.5.0 * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @example * * var object = { * 'a': [{ 'b': 2 }, { 'd': 4 }] * }; * * var other = { * 'a': [{ 'c': 3 }, { 'e': 5 }] * }; * * _.merge(object, other); * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } */ var merge = createAssigner(function(object, source, srcIndex) { baseMerge(object, source, srcIndex); }); /** * This method is like `_.merge` except that it accepts `customizer` which * is invoked to produce the merged values of the destination and source * properties. If `customizer` returns `undefined`, merging is handled by the * method instead. The `customizer` is invoked with six arguments: * (objValue, srcValue, key, object, source, stack). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The destination object. * @param {...Object} sources The source objects. * @param {Function} customizer The function to customize assigned values. * @returns {Object} Returns `object`. * @example * * function customizer(objValue, srcValue) { * if (_.isArray(objValue)) { * return objValue.concat(srcValue); * } * } * * var object = { 'a': [1], 'b': [2] }; * var other = { 'a': [3], 'b': [4] }; * * _.mergeWith(object, other, customizer); * // => { 'a': [1, 3], 'b': [2, 4] } */ var mergeWith = createAssigner(function(object, source, srcIndex, customizer) { baseMerge(object, source, srcIndex, customizer); }); /** * The opposite of `_.pick`; this method creates an object composed of the * own and inherited enumerable property paths of `object` that are not omitted. * * **Note:** This method is considerably slower than `_.pick`. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The source object. * @param {...(string|string[])} [paths] The property paths to omit. * @returns {Object} Returns the new object. * @example * * var object = { 'a': 1, 'b': '2', 'c': 3 }; * * _.omit(object, ['a', 'c']); * // => { 'b': '2' } */ var omit = flatRest(function(object, paths) { var result = {}; if (object == null) { return result; } var isDeep = false; paths = arrayMap(paths, function(path) { path = castPath(path, object); isDeep || (isDeep = path.length > 1); return path; }); copyObject(object, getAllKeysIn(object), result); if (isDeep) { result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone); } var length = paths.length; while (length--) { baseUnset(result, paths[length]); } return result; }); /** * The opposite of `_.pickBy`; this method creates an object composed of * the own and inherited enumerable string keyed properties of `object` that * `predicate` doesn't return truthy for. The predicate is invoked with two * arguments: (value, key). * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The source object. * @param {Function} [predicate=_.identity] The function invoked per property. * @returns {Object} Returns the new object. * @example * * var object = { 'a': 1, 'b': '2', 'c': 3 }; * * _.omitBy(object, _.isNumber); * // => { 'b': '2' } */ function omitBy(object, predicate) { return pickBy(object, negate(getIteratee(predicate))); } /** * Creates an object composed of the picked `object` properties. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The source object. * @param {...(string|string[])} [paths] The property paths to pick. * @returns {Object} Returns the new object. * @example * * var object = { 'a': 1, 'b': '2', 'c': 3 }; * * _.pick(object, ['a', 'c']); * // => { 'a': 1, 'c': 3 } */ var pick = flatRest(function(object, paths) { return object == null ? {} : basePick(object, paths); }); /** * Creates an object composed of the `object` properties `predicate` returns * truthy for. The predicate is invoked with two arguments: (value, key). * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The source object. * @param {Function} [predicate=_.identity] The function invoked per property. * @returns {Object} Returns the new object. * @example * * var object = { 'a': 1, 'b': '2', 'c': 3 }; * * _.pickBy(object, _.isNumber); * // => { 'a': 1, 'c': 3 } */ function pickBy(object, predicate) { if (object == null) { return {}; } var props = arrayMap(getAllKeysIn(object), function(prop) { return [prop]; }); predicate = getIteratee(predicate); return basePickBy(object, props, function(value, path) { return predicate(value, path[0]); }); } /** * This method is like `_.get` except that if the resolved value is a * function it's invoked with the `this` binding of its parent object and * its result is returned. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path of the property to resolve. * @param {*} [defaultValue] The value returned for `undefined` resolved values. * @returns {*} Returns the resolved value. * @example * * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; * * _.result(object, 'a[0].b.c1'); * // => 3 * * _.result(object, 'a[0].b.c2'); * // => 4 * * _.result(object, 'a[0].b.c3', 'default'); * // => 'default' * * _.result(object, 'a[0].b.c3', _.constant('default')); * // => 'default' */ function result(object, path, defaultValue) { path = castPath(path, object); var index = -1, length = path.length; // Ensure the loop is entered when path is empty. if (!length) { length = 1; object = undefined; } while (++index < length) { var value = object == null ? undefined : object[toKey(path[index])]; if (value === undefined) { index = length; value = defaultValue; } object = isFunction(value) ? value.call(object) : value; } return object; } /** * Sets the value at `path` of `object`. If a portion of `path` doesn't exist, * it's created. Arrays are created for missing index properties while objects * are created for all other missing properties. Use `_.setWith` to customize * `path` creation. * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 3.7.0 * @category Object * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to set. * @param {*} value The value to set. * @returns {Object} Returns `object`. * @example * * var object = { 'a': [{ 'b': { 'c': 3 } }] }; * * _.set(object, 'a[0].b.c', 4); * console.log(object.a[0].b.c); * // => 4 * * _.set(object, ['x', '0', 'y', 'z'], 5); * console.log(object.x[0].y.z); * // => 5 */ function set(object, path, value) { return object == null ? object : baseSet(object, path, value); } /** * This method is like `_.set` except that it accepts `customizer` which is * invoked to produce the objects of `path`. If `customizer` returns `undefined` * path creation is handled by the method instead. The `customizer` is invoked * with three arguments: (nsValue, key, nsObject). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to set. * @param {*} value The value to set. * @param {Function} [customizer] The function to customize assigned values. * @returns {Object} Returns `object`. * @example * * var object = {}; * * _.setWith(object, '[0][1]', 'a', Object); * // => { '0': { '1': 'a' } } */ function setWith(object, path, value, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined; return object == null ? object : baseSet(object, path, value, customizer); } /** * Creates an array of own enumerable string keyed-value pairs for `object` * which can be consumed by `_.fromPairs`. If `object` is a map or set, its * entries are returned. * * @static * @memberOf _ * @since 4.0.0 * @alias entries * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the key-value pairs. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.toPairs(new Foo); * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed) */ var toPairs = createToPairs(keys); /** * Creates an array of own and inherited enumerable string keyed-value pairs * for `object` which can be consumed by `_.fromPairs`. If `object` is a map * or set, its entries are returned. * * @static * @memberOf _ * @since 4.0.0 * @alias entriesIn * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the key-value pairs. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.toPairsIn(new Foo); * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed) */ var toPairsIn = createToPairs(keysIn); /** * An alternative to `_.reduce`; this method transforms `object` to a new * `accumulator` object which is the result of running each of its own * enumerable string keyed properties thru `iteratee`, with each invocation * potentially mutating the `accumulator` object. If `accumulator` is not * provided, a new object with the same `[[Prototype]]` will be used. The * iteratee is invoked with four arguments: (accumulator, value, key, object). * Iteratee functions may exit iteration early by explicitly returning `false`. * * @static * @memberOf _ * @since 1.3.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [accumulator] The custom accumulator value. * @returns {*} Returns the accumulated value. * @example * * _.transform([2, 3, 4], function(result, n) { * result.push(n *= n); * return n % 2 == 0; * }, []); * // => [4, 9] * * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { * (result[value] || (result[value] = [])).push(key); * }, {}); * // => { '1': ['a', 'c'], '2': ['b'] } */ function transform(object, iteratee, accumulator) { var isArr = isArray(object), isArrLike = isArr || isBuffer(object) || isTypedArray(object); iteratee = getIteratee(iteratee, 4); if (accumulator == null) { var Ctor = object && object.constructor; if (isArrLike) { accumulator = isArr ? new Ctor : []; } else if (isObject(object)) { accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {}; } else { accumulator = {}; } } (isArrLike ? arrayEach : baseForOwn)(object, function(value, index, object) { return iteratee(accumulator, value, index, object); }); return accumulator; } /** * Removes the property at `path` of `object`. * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to unset. * @returns {boolean} Returns `true` if the property is deleted, else `false`. * @example * * var object = { 'a': [{ 'b': { 'c': 7 } }] }; * _.unset(object, 'a[0].b.c'); * // => true * * console.log(object); * // => { 'a': [{ 'b': {} }] }; * * _.unset(object, ['a', '0', 'b', 'c']); * // => true * * console.log(object); * // => { 'a': [{ 'b': {} }] }; */ function unset(object, path) { return object == null ? true : baseUnset(object, path); } /** * This method is like `_.set` except that accepts `updater` to produce the * value to set. Use `_.updateWith` to customize `path` creation. The `updater` * is invoked with one argument: (value). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.6.0 * @category Object * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to set. * @param {Function} updater The function to produce the updated value. * @returns {Object} Returns `object`. * @example * * var object = { 'a': [{ 'b': { 'c': 3 } }] }; * * _.update(object, 'a[0].b.c', function(n) { return n * n; }); * console.log(object.a[0].b.c); * // => 9 * * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; }); * console.log(object.x[0].y.z); * // => 0 */ function update(object, path, updater) { return object == null ? object : baseUpdate(object, path, castFunction(updater)); } /** * This method is like `_.update` except that it accepts `customizer` which is * invoked to produce the objects of `path`. If `customizer` returns `undefined` * path creation is handled by the method instead. The `customizer` is invoked * with three arguments: (nsValue, key, nsObject). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.6.0 * @category Object * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to set. * @param {Function} updater The function to produce the updated value. * @param {Function} [customizer] The function to customize assigned values. * @returns {Object} Returns `object`. * @example * * var object = {}; * * _.updateWith(object, '[0][1]', _.constant('a'), Object); * // => { '0': { '1': 'a' } } */ function updateWith(object, path, updater, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined; return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer); } /** * Creates an array of the own enumerable string keyed property values of `object`. * * **Note:** Non-object values are coerced to objects. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property values. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.values(new Foo); * // => [1, 2] (iteration order is not guaranteed) * * _.values('hi'); * // => ['h', 'i'] */ function values(object) { return object == null ? [] : baseValues(object, keys(object)); } /** * Creates an array of the own and inherited enumerable string keyed property * values of `object`. * * **Note:** Non-object values are coerced to objects. * * @static * @memberOf _ * @since 3.0.0 * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property values. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.valuesIn(new Foo); * // => [1, 2, 3] (iteration order is not guaranteed) */ function valuesIn(object) { return object == null ? [] : baseValues(object, keysIn(object)); } /*------------------------------------------------------------------------*/ /** * Clamps `number` within the inclusive `lower` and `upper` bounds. * * @static * @memberOf _ * @since 4.0.0 * @category Number * @param {number} number The number to clamp. * @param {number} [lower] The lower bound. * @param {number} upper The upper bound. * @returns {number} Returns the clamped number. * @example * * _.clamp(-10, -5, 5); * // => -5 * * _.clamp(10, -5, 5); * // => 5 */ function clamp(number, lower, upper) { if (upper === undefined) { upper = lower; lower = undefined; } if (upper !== undefined) { upper = toNumber(upper); upper = upper === upper ? upper : 0; } if (lower !== undefined) { lower = toNumber(lower); lower = lower === lower ? lower : 0; } return baseClamp(toNumber(number), lower, upper); } /** * Checks if `n` is between `start` and up to, but not including, `end`. If * `end` is not specified, it's set to `start` with `start` then set to `0`. * If `start` is greater than `end` the params are swapped to support * negative ranges. * * @static * @memberOf _ * @since 3.3.0 * @category Number * @param {number} number The number to check. * @param {number} [start=0] The start of the range. * @param {number} end The end of the range. * @returns {boolean} Returns `true` if `number` is in the range, else `false`. * @see _.range, _.rangeRight * @example * * _.inRange(3, 2, 4); * // => true * * _.inRange(4, 8); * // => true * * _.inRange(4, 2); * // => false * * _.inRange(2, 2); * // => false * * _.inRange(1.2, 2); * // => true * * _.inRange(5.2, 4); * // => false * * _.inRange(-3, -2, -6); * // => true */ function inRange(number, start, end) { start = toFinite(start); if (end === undefined) { end = start; start = 0; } else { end = toFinite(end); } number = toNumber(number); return baseInRange(number, start, end); } /** * Produces a random number between the inclusive `lower` and `upper` bounds. * If only one argument is provided a number between `0` and the given number * is returned. If `floating` is `true`, or either `lower` or `upper` are * floats, a floating-point number is returned instead of an integer. * * **Note:** JavaScript follows the IEEE-754 standard for resolving * floating-point values which can produce unexpected results. * * @static * @memberOf _ * @since 0.7.0 * @category Number * @param {number} [lower=0] The lower bound. * @param {number} [upper=1] The upper bound. * @param {boolean} [floating] Specify returning a floating-point number. * @returns {number} Returns the random number. * @example * * _.random(0, 5); * // => an integer between 0 and 5 * * _.random(5); * // => also an integer between 0 and 5 * * _.random(5, true); * // => a floating-point number between 0 and 5 * * _.random(1.2, 5.2); * // => a floating-point number between 1.2 and 5.2 */ function random(lower, upper, floating) { if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) { upper = floating = undefined; } if (floating === undefined) { if (typeof upper == 'boolean') { floating = upper; upper = undefined; } else if (typeof lower == 'boolean') { floating = lower; lower = undefined; } } if (lower === undefined && upper === undefined) { lower = 0; upper = 1; } else { lower = toFinite(lower); if (upper === undefined) { upper = lower; lower = 0; } else { upper = toFinite(upper); } } if (lower > upper) { var temp = lower; lower = upper; upper = temp; } if (floating || lower % 1 || upper % 1) { var rand = nativeRandom(); return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper); } return baseRandom(lower, upper); } /*------------------------------------------------------------------------*/ /** * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the camel cased string. * @example * * _.camelCase('Foo Bar'); * // => 'fooBar' * * _.camelCase('--foo-bar--'); * // => 'fooBar' * * _.camelCase('__FOO_BAR__'); * // => 'fooBar' */ var camelCase = createCompounder(function(result, word, index) { word = word.toLowerCase(); return result + (index ? capitalize(word) : word); }); /** * Converts the first character of `string` to upper case and the remaining * to lower case. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to capitalize. * @returns {string} Returns the capitalized string. * @example * * _.capitalize('FRED'); * // => 'Fred' */ function capitalize(string) { return upperFirst(toString(string).toLowerCase()); } /** * Deburrs `string` by converting * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) * letters to basic Latin letters and removing * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to deburr. * @returns {string} Returns the deburred string. * @example * * _.deburr('déjà vu'); * // => 'deja vu' */ function deburr(string) { string = toString(string); return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); } /** * Checks if `string` ends with the given target string. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to inspect. * @param {string} [target] The string to search for. * @param {number} [position=string.length] The position to search up to. * @returns {boolean} Returns `true` if `string` ends with `target`, * else `false`. * @example * * _.endsWith('abc', 'c'); * // => true * * _.endsWith('abc', 'b'); * // => false * * _.endsWith('abc', 'b', 2); * // => true */ function endsWith(string, target, position) { string = toString(string); target = baseToString(target); var length = string.length; position = position === undefined ? length : baseClamp(toInteger(position), 0, length); var end = position; position -= target.length; return position >= 0 && string.slice(position, end) == target; } /** * Converts the characters "&", "<", ">", '"', and "'" in `string` to their * corresponding HTML entities. * * **Note:** No other characters are escaped. To escape additional * characters use a third-party library like [_he_](https://mths.be/he). * * Though the ">" character is escaped for symmetry, characters like * ">" and "/" don't need escaping in HTML and have no special meaning * unless they're part of a tag or unquoted attribute value. See * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) * (under "semi-related fun fact") for more details. * * When working with HTML you should always * [quote attribute values](http://wonko.com/post/html-escaping) to reduce * XSS vectors. * * @static * @since 0.1.0 * @memberOf _ * @category String * @param {string} [string=''] The string to escape. * @returns {string} Returns the escaped string. * @example * * _.escape('fred, barney, & pebbles'); * // => 'fred, barney, & pebbles' */ function escape(string) { string = toString(string); return (string && reHasUnescapedHtml.test(string)) ? string.replace(reUnescapedHtml, escapeHtmlChar) : string; } /** * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to escape. * @returns {string} Returns the escaped string. * @example * * _.escapeRegExp('[lodash](https://lodash.com/)'); * // => '\[lodash\]\(https://lodash\.com/\)' */ function escapeRegExp(string) { string = toString(string); return (string && reHasRegExpChar.test(string)) ? string.replace(reRegExpChar, '\\$&') : string; } /** * Converts `string` to * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the kebab cased string. * @example * * _.kebabCase('Foo Bar'); * // => 'foo-bar' * * _.kebabCase('fooBar'); * // => 'foo-bar' * * _.kebabCase('__FOO_BAR__'); * // => 'foo-bar' */ var kebabCase = createCompounder(function(result, word, index) { return result + (index ? '-' : '') + word.toLowerCase(); }); /** * Converts `string`, as space separated words, to lower case. * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the lower cased string. * @example * * _.lowerCase('--Foo-Bar--'); * // => 'foo bar' * * _.lowerCase('fooBar'); * // => 'foo bar' * * _.lowerCase('__FOO_BAR__'); * // => 'foo bar' */ var lowerCase = createCompounder(function(result, word, index) { return result + (index ? ' ' : '') + word.toLowerCase(); }); /** * Converts the first character of `string` to lower case. * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the converted string. * @example * * _.lowerFirst('Fred'); * // => 'fred' * * _.lowerFirst('FRED'); * // => 'fRED' */ var lowerFirst = createCaseFirst('toLowerCase'); /** * Pads `string` on the left and right sides if it's shorter than `length`. * Padding characters are truncated if they can't be evenly divided by `length`. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to pad. * @param {number} [length=0] The padding length. * @param {string} [chars=' '] The string used as padding. * @returns {string} Returns the padded string. * @example * * _.pad('abc', 8); * // => ' abc ' * * _.pad('abc', 8, '_-'); * // => '_-abc_-_' * * _.pad('abc', 3); * // => 'abc' */ function pad(string, length, chars) { string = toString(string); length = toInteger(length); var strLength = length ? stringSize(string) : 0; if (!length || strLength >= length) { return string; } var mid = (length - strLength) / 2; return ( createPadding(nativeFloor(mid), chars) + string + createPadding(nativeCeil(mid), chars) ); } /** * Pads `string` on the right side if it's shorter than `length`. Padding * characters are truncated if they exceed `length`. * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to pad. * @param {number} [length=0] The padding length. * @param {string} [chars=' '] The string used as padding. * @returns {string} Returns the padded string. * @example * * _.padEnd('abc', 6); * // => 'abc ' * * _.padEnd('abc', 6, '_-'); * // => 'abc_-_' * * _.padEnd('abc', 3); * // => 'abc' */ function padEnd(string, length, chars) { string = toString(string); length = toInteger(length); var strLength = length ? stringSize(string) : 0; return (length && strLength < length) ? (string + createPadding(length - strLength, chars)) : string; } /** * Pads `string` on the left side if it's shorter than `length`. Padding * characters are truncated if they exceed `length`. * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to pad. * @param {number} [length=0] The padding length. * @param {string} [chars=' '] The string used as padding. * @returns {string} Returns the padded string. * @example * * _.padStart('abc', 6); * // => ' abc' * * _.padStart('abc', 6, '_-'); * // => '_-_abc' * * _.padStart('abc', 3); * // => 'abc' */ function padStart(string, length, chars) { string = toString(string); length = toInteger(length); var strLength = length ? stringSize(string) : 0; return (length && strLength < length) ? (createPadding(length - strLength, chars) + string) : string; } /** * Converts `string` to an integer of the specified radix. If `radix` is * `undefined` or `0`, a `radix` of `10` is used unless `value` is a * hexadecimal, in which case a `radix` of `16` is used. * * **Note:** This method aligns with the * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`. * * @static * @memberOf _ * @since 1.1.0 * @category String * @param {string} string The string to convert. * @param {number} [radix=10] The radix to interpret `value` by. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {number} Returns the converted integer. * @example * * _.parseInt('08'); * // => 8 * * _.map(['6', '08', '10'], _.parseInt); * // => [6, 8, 10] */ function parseInt(string, radix, guard) { if (guard || radix == null) { radix = 0; } else if (radix) { radix = +radix; } return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0); } /** * Repeats the given string `n` times. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to repeat. * @param {number} [n=1] The number of times to repeat the string. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {string} Returns the repeated string. * @example * * _.repeat('*', 3); * // => '***' * * _.repeat('abc', 2); * // => 'abcabc' * * _.repeat('abc', 0); * // => '' */ function repeat(string, n, guard) { if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) { n = 1; } else { n = toInteger(n); } return baseRepeat(toString(string), n); } /** * Replaces matches for `pattern` in `string` with `replacement`. * * **Note:** This method is based on * [`String#replace`](https://mdn.io/String/replace). * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to modify. * @param {RegExp|string} pattern The pattern to replace. * @param {Function|string} replacement The match replacement. * @returns {string} Returns the modified string. * @example * * _.replace('Hi Fred', 'Fred', 'Barney'); * // => 'Hi Barney' */ function replace() { var args = arguments, string = toString(args[0]); return args.length < 3 ? string : string.replace(args[1], args[2]); } /** * Converts `string` to * [snake case](https://en.wikipedia.org/wiki/Snake_case). * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the snake cased string. * @example * * _.snakeCase('Foo Bar'); * // => 'foo_bar' * * _.snakeCase('fooBar'); * // => 'foo_bar' * * _.snakeCase('--FOO-BAR--'); * // => 'foo_bar' */ var snakeCase = createCompounder(function(result, word, index) { return result + (index ? '_' : '') + word.toLowerCase(); }); /** * Splits `string` by `separator`. * * **Note:** This method is based on * [`String#split`](https://mdn.io/String/split). * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to split. * @param {RegExp|string} separator The separator pattern to split by. * @param {number} [limit] The length to truncate results to. * @returns {Array} Returns the string segments. * @example * * _.split('a-b-c', '-', 2); * // => ['a', 'b'] */ function split(string, separator, limit) { if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) { separator = limit = undefined; } limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0; if (!limit) { return []; } string = toString(string); if (string && ( typeof separator == 'string' || (separator != null && !isRegExp(separator)) )) { separator = baseToString(separator); if (!separator && hasUnicode(string)) { return castSlice(stringToArray(string), 0, limit); } } return string.split(separator, limit); } /** * Converts `string` to * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). * * @static * @memberOf _ * @since 3.1.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the start cased string. * @example * * _.startCase('--foo-bar--'); * // => 'Foo Bar' * * _.startCase('fooBar'); * // => 'Foo Bar' * * _.startCase('__FOO_BAR__'); * // => 'FOO BAR' */ var startCase = createCompounder(function(result, word, index) { return result + (index ? ' ' : '') + upperFirst(word); }); /** * Checks if `string` starts with the given target string. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to inspect. * @param {string} [target] The string to search for. * @param {number} [position=0] The position to search from. * @returns {boolean} Returns `true` if `string` starts with `target`, * else `false`. * @example * * _.startsWith('abc', 'a'); * // => true * * _.startsWith('abc', 'b'); * // => false * * _.startsWith('abc', 'b', 1); * // => true */ function startsWith(string, target, position) { string = toString(string); position = position == null ? 0 : baseClamp(toInteger(position), 0, string.length); target = baseToString(target); return string.slice(position, position + target.length) == target; } /** * Creates a compiled template function that can interpolate data properties * in "interpolate" delimiters, HTML-escape interpolated data properties in * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data * properties may be accessed as free variables in the template. If a setting * object is given, it takes precedence over `_.templateSettings` values. * * **Note:** In the development build `_.template` utilizes * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) * for easier debugging. * * For more information on precompiling templates see * [lodash's custom builds documentation](https://lodash.com/custom-builds). * * For more information on Chrome extension sandboxes see * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). * * @static * @since 0.1.0 * @memberOf _ * @category String * @param {string} [string=''] The template string. * @param {Object} [options={}] The options object. * @param {RegExp} [options.escape=_.templateSettings.escape] * The HTML "escape" delimiter. * @param {RegExp} [options.evaluate=_.templateSettings.evaluate] * The "evaluate" delimiter. * @param {Object} [options.imports=_.templateSettings.imports] * An object to import into the template as free variables. * @param {RegExp} [options.interpolate=_.templateSettings.interpolate] * The "interpolate" delimiter. * @param {string} [options.sourceURL='lodash.templateSources[n]'] * The sourceURL of the compiled template. * @param {string} [options.variable='obj'] * The data object variable name. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Function} Returns the compiled template function. * @example * * // Use the "interpolate" delimiter to create a compiled template. * var compiled = _.template('hello <%= user %>!'); * compiled({ 'user': 'fred' }); * // => 'hello fred!' * * // Use the HTML "escape" delimiter to escape data property values. * var compiled = _.template('<%- value %>'); * compiled({ 'value': '
================================================ FILE: archive/paperclip-playground/src/index.ts ================================================ ================================================ FILE: archive/paperclip-playground/todos.md ================================================ ### Immediate - webpack - bundle to static directory - monaco editor - API - ### Main features - persist data s3 - Setup with Gist as backend ================================================ FILE: archive/paperclip-playground/tsconfig.json ================================================ { "compilerOptions": { "jsx": "react", "outDir": "lib", "target": "es5", "moduleResolution": "node", "module": "esnext", "declaration": true, "allowSyntheticDefaultImports": true, "esModuleInterop": true }, "exclude": ["node_modules", "*-test", "lib", "index.d.ts", "test"], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: archive/paperclip-playground/webpack.config.js ================================================ const path = require("path"); const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); const mode = process.env.NODE_ENV === "production" ? "production" : "development"; const prodMode = mode === "production"; const devMode = mode === "development"; const API_HOST = mode === "development" ? "localhost:3001" : "playground-api.paperclip.dev"; const standalone = process.env.PLAYGROUND_STANDALONE != null; const DEV_OAUTH_CLIENT_IDs = { github: "2cdbfa6c949f0c8cd3f5" }; const PROD_OAUTH_CLIENT_IDs = { github: "1c47b5853e6d87769161" }; const OAUTH_CLIENT_IDs = prodMode ? PROD_OAUTH_CLIENT_IDs : DEV_OAUTH_CLIENT_IDs; const plugins = [ new HtmlWebpackPlugin({ publicPath: "/", title: "Paperclip Playground", template: path.join(__dirname, "src", "index.html") }), new webpack.ProvidePlugin({ process: "process/browser" }), new webpack.DefinePlugin({ "process.env.API_HOST": JSON.stringify(API_HOST), "process.env.GITHUB_CLIENT_ID": JSON.stringify(OAUTH_CLIENT_IDs.github), "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }) ]; plugins.push( new MiniCssExtractPlugin({ filename: devMode ? "[name].css" : "[name]-[contenthash].css", chunkFilename: devMode ? "[id].css" : "[id]-[contenthash].css" }) ); if (standalone) { plugins.push( new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }) ); } module.exports = { mode, entry: "./src/frontend/entry.tsx", output: { filename: devMode ? `[name].js` : "[name]-[contenthash].js", path: standalone ? path.resolve(__dirname, "standalone-dist") : path.resolve(__dirname, "dist"), publicPath: "/" }, experiments: { asyncWebAssembly: true }, devtool: false, plugins, resolve: { extensions: [".tsx", ".ts", ".js"], alias: { os: "os-browserify/browser", url: require.resolve("url"), path: require.resolve("path-browserify"), events: require.resolve("events"), react: require.resolve("react"), "react-dom": require.resolve("react-dom") } }, externals: { chokidar: "{}", fs: "{}" }, module: { rules: [ { test: /\.(ts|tsx)?$/, loader: "ts-loader", include: [path.resolve(__dirname, "src"), path.resolve(__dirname, "..")] }, { test: /\.pc$/, loader: "paperclip-loader", include: [ path.resolve(__dirname, "src"), path.resolve(__dirname, "..") ], options: { config: require("./paperclip.config.json") } }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] }, { test: /\.(png|jpe?g|gif|ttf|svg)$/i, use: [ !prodMode ? { loader: "url-loader", options: { limit: Infinity } } : { loader: "file-loader" } ] } ] }, optimization: standalone ? { minimizer: [new TerserPlugin(), new CssMinimizerPlugin()], minimize: prodMode } : { minimizer: [new TerserPlugin(), new CssMinimizerPlugin()], runtimeChunk: true, minimize: prodMode, splitChunks: { maxInitialRequests: Infinity, cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/ } }, chunks: "all", minChunks: 1, // make sure that chunks are larger than 400kb minSize: 1000 * 200, // make sure that chunks are smaller than 1.5 MB maxSize: 1000 * 1500, name: false } } }; ================================================ FILE: examples/react-basic/README.md ================================================ [![Edit React Demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://githubbox.com/paperclipui/paperclip/tree/master/examples/react-basic) This is a basic example of how you can use Paperclip with React. To get started, just run `yarn install && yarn start`. React Usage: https://paperclip.dev/docs/usage-react ================================================ FILE: examples/react-basic/package.json ================================================ { "name": "@paperclip-ui/examples-react-basic", "version": "18.5.6", "private": true, "description": "", "main": "index.js", "scripts": { "start": "yarn build:paperclip && concurrently \"webpack serve\" \"yarn build:paperclip --watch\"", "build:paperclip": "paperclip build" }, "author": "", "license": "ISC", "devDependencies": { "@paperclip-ui/cli": "^18.5.6", "@paperclip-ui/compiler-react": "^18.5.6", "@webpack-cli/init": "^1.1.3", "@webpack-cli/serve": "^1.6.0", "concurrently": "^6.5.1", "css-loader": "^6.5.1", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.0", "mini-css-extract-plugin": "^2.4.5", "paperclip-loader": "^18.5.6", "ts-loader": "^9.2.6", "webpack": "^5.65.0", "webpack-cli": "^4.9.1", "webpack-dev-server": "^4.7.1" }, "dependencies": { "react": "^17.0.2", "react-dom": "^17.0.2" } } ================================================ FILE: examples/react-basic/paperclip.config.json ================================================ { "compilerOptions": [{ "emit": ["d.ts"] }], "srcDir": "./src" } ================================================ FILE: examples/react-basic/sandbox.config.json ================================================ { "template": "node", "view": "browser" } ================================================ FILE: examples/react-basic/src/CustomGroceryList.pc ================================================ {children} Something ================================================ FILE: examples/react-basic/src/GroceryList.pc ================================================
    {children}
  • {children}
  • Milk Eggs Ham Milk Eggs Ham ================================================ FILE: examples/react-basic/src/GroceryList.tsx ================================================ // PC files are compiled to plain code & can // be imported as regular modules import React from "react"; import * as defaultStyles from "./GroceryList.pc"; export type GroceryListProps = { styles?: Partial; groceries: string[]; }; export function GroceryList({ groceries, styles: styleOverrides }: GroceryListProps) { const styles = { ...defaultStyles, ...styleOverrides }; return ( {groceries.map(item => ( {item} ))} ); } ================================================ FILE: examples/react-basic/src/breakpoints.pc ================================================ ================================================ FILE: examples/react-basic/src/entry.tsx ================================================ import React from "react"; import ReactDOM from "react-dom"; import { GroceryList } from "./GroceryList"; import * as groceryListStyles from "./CustomGroceryList.pc"; ReactDOM.render( <> {/* example of how you can theme components that use PC styles */} , document.getElementById("app") ); ================================================ FILE: examples/react-basic/src/index.html ================================================ Basic Paperclip example with React
    ================================================ FILE: examples/react-basic/tsconfig.json ================================================ { "compilerOptions": { "jsx": "react", "outDir": "lib", "target": "es5", "moduleResolution": "node", "module": "commonjs", "declaration": true, "allowSyntheticDefaultImports": true, "esModuleInterop": true }, "exclude": ["node_modules", "*-test", "lib", "index.d.ts", "test"], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: examples/react-basic/webpack.config.js ================================================ const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { mode: "development", entry: "./src/entry.tsx", output: { filename: "[name]-[contenthash].js", path: path.resolve(__dirname, "dist"), publicPath: "/" }, plugins: [ new MiniCssExtractPlugin(), new HtmlWebpackPlugin({ publicPath: "/", title: "React Demo", template: path.resolve(__dirname, "src", "index.html") }) ], resolve: { extensions: [".tsx", ".ts", ".js"] }, module: { rules: [ { test: /\.(ts|tsx)?$/, loader: "ts-loader", include: [path.resolve(__dirname, "src")], exclude: [] }, { test: /\.pc$/, loader: "paperclip-loader", include: [path.resolve(__dirname, "src")], options: { config: require("./paperclip.config.json") } }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] }, { test: /\.(png|jpe?g|gif|ttf|svg)$/i, use: [ { loader: "file-loader" } ] } ] } }; ================================================ FILE: examples/syntax-basic/README.md ================================================ [![Edit cocky-brook-jbeo2](https://codesandbox.io/static/img/play-codesandbox.svg)](https://githubbox.com/paperclipui/paperclip/tree/master/examples/syntax-basic) Welcome! This is a basic `Hello World` [Paperclip](https://paperclip.dev/) app that integrates with **React**, and **Webpack**. You can open the designer by running `yarn designer` in a new terminal window, this also works in Codesandbox. - Paperclip Syntax: https://paperclip.dev/docs/usage-syntax - React Usage: https://paperclip.dev/docs/usage-react ================================================ FILE: examples/syntax-basic/package.json ================================================ { "name": "@paperclip-ui/example-basics", "version": "18.5.6", "description": "", "main": "src/index.js", "private": true, "scripts": { "start": "paperclip designer --canvas-file=./src/hello.pc", "build": "paperclip build" }, "devDependencies": { "@paperclip-ui/cli": "^18.5.6", "@paperclip-ui/compiler-react": "^18.5.6", "@paperclip-ui/core": "^18.5.6" }, "keywords": [] } ================================================ FILE: examples/syntax-basic/paperclip.config.json ================================================ { "srcDir": "./src", "moduleDirs": ["./node_modules"] } ================================================ FILE: examples/syntax-basic/sandbox.config.json ================================================ { "template": "node", "view": "browser" } ================================================ FILE: examples/syntax-basic/src/hello.pc ================================================
    I'm purple!
    ================================================ FILE: examples/tailwind/.gitignore ================================================ lib/ dist/ *.scoped.css src/styles/ ================================================ FILE: examples/tailwind/README.md ================================================ [![Edit cocky-brook-jbeo2](https://codesandbox.io/static/img/play-codesandbox.svg)](https://githubbox.com/paperclipui/paperclip/tree/master/examples/tailwind) This is a basic of Paperclip using [Tailwind](https://tailwindcss.com/). Just run `yarn install` and then `yarn start` to run. - Using third-party CSS guide: https://paperclip.dev/docs/guide-third-party-libraries ================================================ FILE: examples/tailwind/package.json ================================================ { "name": "@paperclip-ui/paperclip-tailwind", "version": "18.5.6", "main": "index.js", "license": "MIT", "private": true, "scripts": { "start": "yarn build:css && concurrently \"yarn build:css --watch\" \"paperclip designer --canvas-file=./src/index.pc\"", "build": "yarn build:css && yarn build:pc", "build:watch": "concurrently \"yarn build:pc --watch\" \"yarn build:css --watch\"", "build:pc": "paperclip build", "build:css": "tailwindcss -i ./src/tailwind.scss -o ./src/styles/tailwind.css" }, "devDependencies": { "@paperclip-ui/cli": "^18.5.6", "@paperclip-ui/compiler-react": "^18.5.6", "@paperclip-ui/core": "^18.5.6", "concurrently": "^6.5.1", "paperclip-loader": "^18.5.6", "tailwindcss": "^3.0.7" } } ================================================ FILE: examples/tailwind/paperclip.config.json ================================================ { "compilerOptions": { "target": "react", "outDir": "lib", "importAssetsAsModules": true }, "srcDir": "./src", "moduleDirs": ["./node_modules"] } ================================================ FILE: examples/tailwind/sandbox.config.json ================================================ { "template": "node", "view": "browser" } ================================================ FILE: examples/tailwind/src/index.pc ================================================

    Sign into this thing

    Don't have an account? Create One
    Project Client Users Status Actions
    React Project
    Eshal Rosas
    Active
    Vue Project
    Anita Rodriquez
    Completed
    Angular Project
    Taylan Bush
    Scheduled
    Laravel Project
    Tarik Novak
    Pending
    GIT Project
    Oscar Howard
    Active
    NodeJS Project
    Melisa Moon
    Scheduled
    JavaScript Project
    Cora Key
    Pending
    PHP Project
    Kylan Dorsey
    Completed

    JED DYLAN LEE

    Admin
    ================================================ FILE: examples/tailwind/src/tailwind.scss ================================================ @tailwind base; @tailwind components; @tailwind utilities; ================================================ FILE: examples/tailwind/tailwind.config.js ================================================ module.exports = { content: ["./src/**/*.pc"], theme: { extend: {} }, plugins: [] }; ================================================ FILE: examples/tailwind-and-animate/.gitignore ================================================ tailwind.css ================================================ FILE: examples/tailwind-and-animate/README.md ================================================ [![Edit React Demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://githubbox.com/paperclipui/paperclip/tree/master/examples/tailwind-and-animate) Tailwind + Animate.css example ================================================ FILE: examples/tailwind-and-animate/css-modules/animate.css ================================================ @charset "UTF-8"; /*! * animate.css - https://animate.style/ * Version - 4.1.1 * Licensed under the MIT license - http://opensource.org/licenses/MIT * * Copyright (c) 2020 Animate.css */ :root { --animate-duration: 1s; --animate-delay: 1s; --animate-repeat: 1; } .animate__animated { -webkit-animation-duration: 1s; animation-duration: 1s; -webkit-animation-duration: var(--animate-duration); animation-duration: var(--animate-duration); -webkit-animation-fill-mode: both; animation-fill-mode: both; } .animate__animated.animate__infinite { -webkit-animation-iteration-count: infinite; animation-iteration-count: infinite; } .animate__animated.animate__repeat-1 { -webkit-animation-iteration-count: 1; animation-iteration-count: 1; -webkit-animation-iteration-count: var(--animate-repeat); animation-iteration-count: var(--animate-repeat); } .animate__animated.animate__repeat-2 { -webkit-animation-iteration-count: calc(1 * 2); animation-iteration-count: calc(1 * 2); -webkit-animation-iteration-count: calc(var(--animate-repeat) * 2); animation-iteration-count: calc(var(--animate-repeat) * 2); } .animate__animated.animate__repeat-3 { -webkit-animation-iteration-count: calc(1 * 3); animation-iteration-count: calc(1 * 3); -webkit-animation-iteration-count: calc(var(--animate-repeat) * 3); animation-iteration-count: calc(var(--animate-repeat) * 3); } .animate__animated.animate__delay-1s { -webkit-animation-delay: 1s; animation-delay: 1s; -webkit-animation-delay: var(--animate-delay); animation-delay: var(--animate-delay); } .animate__animated.animate__delay-2s { -webkit-animation-delay: calc(1s * 2); animation-delay: calc(1s * 2); -webkit-animation-delay: calc(var(--animate-delay) * 2); animation-delay: calc(var(--animate-delay) * 2); } .animate__animated.animate__delay-3s { -webkit-animation-delay: calc(1s * 3); animation-delay: calc(1s * 3); -webkit-animation-delay: calc(var(--animate-delay) * 3); animation-delay: calc(var(--animate-delay) * 3); } .animate__animated.animate__delay-4s { -webkit-animation-delay: calc(1s * 4); animation-delay: calc(1s * 4); -webkit-animation-delay: calc(var(--animate-delay) * 4); animation-delay: calc(var(--animate-delay) * 4); } .animate__animated.animate__delay-5s { -webkit-animation-delay: calc(1s * 5); animation-delay: calc(1s * 5); -webkit-animation-delay: calc(var(--animate-delay) * 5); animation-delay: calc(var(--animate-delay) * 5); } .animate__animated.animate__faster { -webkit-animation-duration: calc(1s / 2); animation-duration: calc(1s / 2); -webkit-animation-duration: calc(var(--animate-duration) / 2); animation-duration: calc(var(--animate-duration) / 2); } .animate__animated.animate__fast { -webkit-animation-duration: calc(1s * 0.8); animation-duration: calc(1s * 0.8); -webkit-animation-duration: calc(var(--animate-duration) * 0.8); animation-duration: calc(var(--animate-duration) * 0.8); } .animate__animated.animate__slow { -webkit-animation-duration: calc(1s * 2); animation-duration: calc(1s * 2); -webkit-animation-duration: calc(var(--animate-duration) * 2); animation-duration: calc(var(--animate-duration) * 2); } .animate__animated.animate__slower { -webkit-animation-duration: calc(1s * 3); animation-duration: calc(1s * 3); -webkit-animation-duration: calc(var(--animate-duration) * 3); animation-duration: calc(var(--animate-duration) * 3); } @media print, (prefers-reduced-motion: reduce) { .animate__animated { -webkit-animation-duration: 1ms !important; animation-duration: 1ms !important; -webkit-transition-duration: 1ms !important; transition-duration: 1ms !important; -webkit-animation-iteration-count: 1 !important; animation-iteration-count: 1 !important; } .animate__animated[class*="Out"] { opacity: 0; } } /* Attention seekers */ @-webkit-keyframes bounce { from, 20%, 53%, to { -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 40%, 43% { -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); -webkit-transform: translate3d(0, -30px, 0) scaleY(1.1); transform: translate3d(0, -30px, 0) scaleY(1.1); } 70% { -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); -webkit-transform: translate3d(0, -15px, 0) scaleY(1.05); transform: translate3d(0, -15px, 0) scaleY(1.05); } 80% { -webkit-transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); -webkit-transform: translate3d(0, 0, 0) scaleY(0.95); transform: translate3d(0, 0, 0) scaleY(0.95); } 90% { -webkit-transform: translate3d(0, -4px, 0) scaleY(1.02); transform: translate3d(0, -4px, 0) scaleY(1.02); } } @keyframes bounce { from, 20%, 53%, to { -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 40%, 43% { -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); -webkit-transform: translate3d(0, -30px, 0) scaleY(1.1); transform: translate3d(0, -30px, 0) scaleY(1.1); } 70% { -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); -webkit-transform: translate3d(0, -15px, 0) scaleY(1.05); transform: translate3d(0, -15px, 0) scaleY(1.05); } 80% { -webkit-transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); -webkit-transform: translate3d(0, 0, 0) scaleY(0.95); transform: translate3d(0, 0, 0) scaleY(0.95); } 90% { -webkit-transform: translate3d(0, -4px, 0) scaleY(1.02); transform: translate3d(0, -4px, 0) scaleY(1.02); } } .animate__bounce { -webkit-animation-name: bounce; animation-name: bounce; -webkit-transform-origin: center bottom; transform-origin: center bottom; } @-webkit-keyframes flash { from, 50%, to { opacity: 1; } 25%, 75% { opacity: 0; } } @keyframes flash { from, 50%, to { opacity: 1; } 25%, 75% { opacity: 0; } } .animate__flash { -webkit-animation-name: flash; animation-name: flash; } /* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ @-webkit-keyframes pulse { from { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 50% { -webkit-transform: scale3d(1.05, 1.05, 1.05); transform: scale3d(1.05, 1.05, 1.05); } to { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } @keyframes pulse { from { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 50% { -webkit-transform: scale3d(1.05, 1.05, 1.05); transform: scale3d(1.05, 1.05, 1.05); } to { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } .animate__pulse { -webkit-animation-name: pulse; animation-name: pulse; -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; } @-webkit-keyframes rubberBand { from { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 30% { -webkit-transform: scale3d(1.25, 0.75, 1); transform: scale3d(1.25, 0.75, 1); } 40% { -webkit-transform: scale3d(0.75, 1.25, 1); transform: scale3d(0.75, 1.25, 1); } 50% { -webkit-transform: scale3d(1.15, 0.85, 1); transform: scale3d(1.15, 0.85, 1); } 65% { -webkit-transform: scale3d(0.95, 1.05, 1); transform: scale3d(0.95, 1.05, 1); } 75% { -webkit-transform: scale3d(1.05, 0.95, 1); transform: scale3d(1.05, 0.95, 1); } to { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } @keyframes rubberBand { from { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 30% { -webkit-transform: scale3d(1.25, 0.75, 1); transform: scale3d(1.25, 0.75, 1); } 40% { -webkit-transform: scale3d(0.75, 1.25, 1); transform: scale3d(0.75, 1.25, 1); } 50% { -webkit-transform: scale3d(1.15, 0.85, 1); transform: scale3d(1.15, 0.85, 1); } 65% { -webkit-transform: scale3d(0.95, 1.05, 1); transform: scale3d(0.95, 1.05, 1); } 75% { -webkit-transform: scale3d(1.05, 0.95, 1); transform: scale3d(1.05, 0.95, 1); } to { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } .animate__rubberBand { -webkit-animation-name: rubberBand; animation-name: rubberBand; } @-webkit-keyframes shakeX { from, to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 10%, 30%, 50%, 70%, 90% { -webkit-transform: translate3d(-10px, 0, 0); transform: translate3d(-10px, 0, 0); } 20%, 40%, 60%, 80% { -webkit-transform: translate3d(10px, 0, 0); transform: translate3d(10px, 0, 0); } } @keyframes shakeX { from, to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 10%, 30%, 50%, 70%, 90% { -webkit-transform: translate3d(-10px, 0, 0); transform: translate3d(-10px, 0, 0); } 20%, 40%, 60%, 80% { -webkit-transform: translate3d(10px, 0, 0); transform: translate3d(10px, 0, 0); } } .animate__shakeX { -webkit-animation-name: shakeX; animation-name: shakeX; } @-webkit-keyframes shakeY { from, to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 10%, 30%, 50%, 70%, 90% { -webkit-transform: translate3d(0, -10px, 0); transform: translate3d(0, -10px, 0); } 20%, 40%, 60%, 80% { -webkit-transform: translate3d(0, 10px, 0); transform: translate3d(0, 10px, 0); } } @keyframes shakeY { from, to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 10%, 30%, 50%, 70%, 90% { -webkit-transform: translate3d(0, -10px, 0); transform: translate3d(0, -10px, 0); } 20%, 40%, 60%, 80% { -webkit-transform: translate3d(0, 10px, 0); transform: translate3d(0, 10px, 0); } } .animate__shakeY { -webkit-animation-name: shakeY; animation-name: shakeY; } @-webkit-keyframes headShake { 0% { -webkit-transform: translateX(0); transform: translateX(0); } 6.5% { -webkit-transform: translateX(-6px) rotateY(-9deg); transform: translateX(-6px) rotateY(-9deg); } 18.5% { -webkit-transform: translateX(5px) rotateY(7deg); transform: translateX(5px) rotateY(7deg); } 31.5% { -webkit-transform: translateX(-3px) rotateY(-5deg); transform: translateX(-3px) rotateY(-5deg); } 43.5% { -webkit-transform: translateX(2px) rotateY(3deg); transform: translateX(2px) rotateY(3deg); } 50% { -webkit-transform: translateX(0); transform: translateX(0); } } @keyframes headShake { 0% { -webkit-transform: translateX(0); transform: translateX(0); } 6.5% { -webkit-transform: translateX(-6px) rotateY(-9deg); transform: translateX(-6px) rotateY(-9deg); } 18.5% { -webkit-transform: translateX(5px) rotateY(7deg); transform: translateX(5px) rotateY(7deg); } 31.5% { -webkit-transform: translateX(-3px) rotateY(-5deg); transform: translateX(-3px) rotateY(-5deg); } 43.5% { -webkit-transform: translateX(2px) rotateY(3deg); transform: translateX(2px) rotateY(3deg); } 50% { -webkit-transform: translateX(0); transform: translateX(0); } } .animate__headShake { -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; -webkit-animation-name: headShake; animation-name: headShake; } @-webkit-keyframes swing { 20% { -webkit-transform: rotate3d(0, 0, 1, 15deg); transform: rotate3d(0, 0, 1, 15deg); } 40% { -webkit-transform: rotate3d(0, 0, 1, -10deg); transform: rotate3d(0, 0, 1, -10deg); } 60% { -webkit-transform: rotate3d(0, 0, 1, 5deg); transform: rotate3d(0, 0, 1, 5deg); } 80% { -webkit-transform: rotate3d(0, 0, 1, -5deg); transform: rotate3d(0, 0, 1, -5deg); } to { -webkit-transform: rotate3d(0, 0, 1, 0deg); transform: rotate3d(0, 0, 1, 0deg); } } @keyframes swing { 20% { -webkit-transform: rotate3d(0, 0, 1, 15deg); transform: rotate3d(0, 0, 1, 15deg); } 40% { -webkit-transform: rotate3d(0, 0, 1, -10deg); transform: rotate3d(0, 0, 1, -10deg); } 60% { -webkit-transform: rotate3d(0, 0, 1, 5deg); transform: rotate3d(0, 0, 1, 5deg); } 80% { -webkit-transform: rotate3d(0, 0, 1, -5deg); transform: rotate3d(0, 0, 1, -5deg); } to { -webkit-transform: rotate3d(0, 0, 1, 0deg); transform: rotate3d(0, 0, 1, 0deg); } } .animate__swing { -webkit-transform-origin: top center; transform-origin: top center; -webkit-animation-name: swing; animation-name: swing; } @-webkit-keyframes tada { from { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 10%, 20% { -webkit-transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); } 30%, 50%, 70%, 90% { -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); } 40%, 60%, 80% { -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); } to { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } @keyframes tada { from { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 10%, 20% { -webkit-transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); } 30%, 50%, 70%, 90% { -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); } 40%, 60%, 80% { -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); } to { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } .animate__tada { -webkit-animation-name: tada; animation-name: tada; } /* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ @-webkit-keyframes wobble { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 15% { -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); } 30% { -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); } 45% { -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); } 60% { -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); } 75% { -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes wobble { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 15% { -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); } 30% { -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); } 45% { -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); } 60% { -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); } 75% { -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__wobble { -webkit-animation-name: wobble; animation-name: wobble; } @-webkit-keyframes jello { from, 11.1%, to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 22.2% { -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); transform: skewX(-12.5deg) skewY(-12.5deg); } 33.3% { -webkit-transform: skewX(6.25deg) skewY(6.25deg); transform: skewX(6.25deg) skewY(6.25deg); } 44.4% { -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); transform: skewX(-3.125deg) skewY(-3.125deg); } 55.5% { -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); transform: skewX(1.5625deg) skewY(1.5625deg); } 66.6% { -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); transform: skewX(-0.78125deg) skewY(-0.78125deg); } 77.7% { -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); transform: skewX(0.390625deg) skewY(0.390625deg); } 88.8% { -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); transform: skewX(-0.1953125deg) skewY(-0.1953125deg); } } @keyframes jello { from, 11.1%, to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 22.2% { -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); transform: skewX(-12.5deg) skewY(-12.5deg); } 33.3% { -webkit-transform: skewX(6.25deg) skewY(6.25deg); transform: skewX(6.25deg) skewY(6.25deg); } 44.4% { -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); transform: skewX(-3.125deg) skewY(-3.125deg); } 55.5% { -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); transform: skewX(1.5625deg) skewY(1.5625deg); } 66.6% { -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); transform: skewX(-0.78125deg) skewY(-0.78125deg); } 77.7% { -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); transform: skewX(0.390625deg) skewY(0.390625deg); } 88.8% { -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); transform: skewX(-0.1953125deg) skewY(-0.1953125deg); } } .animate__jello { -webkit-animation-name: jello; animation-name: jello; -webkit-transform-origin: center; transform-origin: center; } @-webkit-keyframes heartBeat { 0% { -webkit-transform: scale(1); transform: scale(1); } 14% { -webkit-transform: scale(1.3); transform: scale(1.3); } 28% { -webkit-transform: scale(1); transform: scale(1); } 42% { -webkit-transform: scale(1.3); transform: scale(1.3); } 70% { -webkit-transform: scale(1); transform: scale(1); } } @keyframes heartBeat { 0% { -webkit-transform: scale(1); transform: scale(1); } 14% { -webkit-transform: scale(1.3); transform: scale(1.3); } 28% { -webkit-transform: scale(1); transform: scale(1); } 42% { -webkit-transform: scale(1.3); transform: scale(1.3); } 70% { -webkit-transform: scale(1); transform: scale(1); } } .animate__heartBeat { -webkit-animation-name: heartBeat; animation-name: heartBeat; -webkit-animation-duration: calc(1s * 1.3); animation-duration: calc(1s * 1.3); -webkit-animation-duration: calc(var(--animate-duration) * 1.3); animation-duration: calc(var(--animate-duration) * 1.3); -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; } /* Back entrances */ @-webkit-keyframes backInDown { 0% { -webkit-transform: translateY(-1200px) scale(0.7); transform: translateY(-1200px) scale(0.7); opacity: 0.7; } 80% { -webkit-transform: translateY(0px) scale(0.7); transform: translateY(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } } @keyframes backInDown { 0% { -webkit-transform: translateY(-1200px) scale(0.7); transform: translateY(-1200px) scale(0.7); opacity: 0.7; } 80% { -webkit-transform: translateY(0px) scale(0.7); transform: translateY(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } } .animate__backInDown { -webkit-animation-name: backInDown; animation-name: backInDown; } @-webkit-keyframes backInLeft { 0% { -webkit-transform: translateX(-2000px) scale(0.7); transform: translateX(-2000px) scale(0.7); opacity: 0.7; } 80% { -webkit-transform: translateX(0px) scale(0.7); transform: translateX(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } } @keyframes backInLeft { 0% { -webkit-transform: translateX(-2000px) scale(0.7); transform: translateX(-2000px) scale(0.7); opacity: 0.7; } 80% { -webkit-transform: translateX(0px) scale(0.7); transform: translateX(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } } .animate__backInLeft { -webkit-animation-name: backInLeft; animation-name: backInLeft; } @-webkit-keyframes backInRight { 0% { -webkit-transform: translateX(2000px) scale(0.7); transform: translateX(2000px) scale(0.7); opacity: 0.7; } 80% { -webkit-transform: translateX(0px) scale(0.7); transform: translateX(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } } @keyframes backInRight { 0% { -webkit-transform: translateX(2000px) scale(0.7); transform: translateX(2000px) scale(0.7); opacity: 0.7; } 80% { -webkit-transform: translateX(0px) scale(0.7); transform: translateX(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } } .animate__backInRight { -webkit-animation-name: backInRight; animation-name: backInRight; } @-webkit-keyframes backInUp { 0% { -webkit-transform: translateY(1200px) scale(0.7); transform: translateY(1200px) scale(0.7); opacity: 0.7; } 80% { -webkit-transform: translateY(0px) scale(0.7); transform: translateY(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } } @keyframes backInUp { 0% { -webkit-transform: translateY(1200px) scale(0.7); transform: translateY(1200px) scale(0.7); opacity: 0.7; } 80% { -webkit-transform: translateY(0px) scale(0.7); transform: translateY(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } } .animate__backInUp { -webkit-animation-name: backInUp; animation-name: backInUp; } /* Back exits */ @-webkit-keyframes backOutDown { 0% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } 20% { -webkit-transform: translateY(0px) scale(0.7); transform: translateY(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: translateY(700px) scale(0.7); transform: translateY(700px) scale(0.7); opacity: 0.7; } } @keyframes backOutDown { 0% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } 20% { -webkit-transform: translateY(0px) scale(0.7); transform: translateY(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: translateY(700px) scale(0.7); transform: translateY(700px) scale(0.7); opacity: 0.7; } } .animate__backOutDown { -webkit-animation-name: backOutDown; animation-name: backOutDown; } @-webkit-keyframes backOutLeft { 0% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } 20% { -webkit-transform: translateX(0px) scale(0.7); transform: translateX(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: translateX(-2000px) scale(0.7); transform: translateX(-2000px) scale(0.7); opacity: 0.7; } } @keyframes backOutLeft { 0% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } 20% { -webkit-transform: translateX(0px) scale(0.7); transform: translateX(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: translateX(-2000px) scale(0.7); transform: translateX(-2000px) scale(0.7); opacity: 0.7; } } .animate__backOutLeft { -webkit-animation-name: backOutLeft; animation-name: backOutLeft; } @-webkit-keyframes backOutRight { 0% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } 20% { -webkit-transform: translateX(0px) scale(0.7); transform: translateX(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: translateX(2000px) scale(0.7); transform: translateX(2000px) scale(0.7); opacity: 0.7; } } @keyframes backOutRight { 0% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } 20% { -webkit-transform: translateX(0px) scale(0.7); transform: translateX(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: translateX(2000px) scale(0.7); transform: translateX(2000px) scale(0.7); opacity: 0.7; } } .animate__backOutRight { -webkit-animation-name: backOutRight; animation-name: backOutRight; } @-webkit-keyframes backOutUp { 0% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } 20% { -webkit-transform: translateY(0px) scale(0.7); transform: translateY(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: translateY(-700px) scale(0.7); transform: translateY(-700px) scale(0.7); opacity: 0.7; } } @keyframes backOutUp { 0% { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } 20% { -webkit-transform: translateY(0px) scale(0.7); transform: translateY(0px) scale(0.7); opacity: 0.7; } 100% { -webkit-transform: translateY(-700px) scale(0.7); transform: translateY(-700px) scale(0.7); opacity: 0.7; } } .animate__backOutUp { -webkit-animation-name: backOutUp; animation-name: backOutUp; } /* Bouncing entrances */ @-webkit-keyframes bounceIn { from, 20%, 40%, 60%, 80%, to { -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 0% { opacity: 0; -webkit-transform: scale3d(0.3, 0.3, 0.3); transform: scale3d(0.3, 0.3, 0.3); } 20% { -webkit-transform: scale3d(1.1, 1.1, 1.1); transform: scale3d(1.1, 1.1, 1.1); } 40% { -webkit-transform: scale3d(0.9, 0.9, 0.9); transform: scale3d(0.9, 0.9, 0.9); } 60% { opacity: 1; -webkit-transform: scale3d(1.03, 1.03, 1.03); transform: scale3d(1.03, 1.03, 1.03); } 80% { -webkit-transform: scale3d(0.97, 0.97, 0.97); transform: scale3d(0.97, 0.97, 0.97); } to { opacity: 1; -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } @keyframes bounceIn { from, 20%, 40%, 60%, 80%, to { -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 0% { opacity: 0; -webkit-transform: scale3d(0.3, 0.3, 0.3); transform: scale3d(0.3, 0.3, 0.3); } 20% { -webkit-transform: scale3d(1.1, 1.1, 1.1); transform: scale3d(1.1, 1.1, 1.1); } 40% { -webkit-transform: scale3d(0.9, 0.9, 0.9); transform: scale3d(0.9, 0.9, 0.9); } 60% { opacity: 1; -webkit-transform: scale3d(1.03, 1.03, 1.03); transform: scale3d(1.03, 1.03, 1.03); } 80% { -webkit-transform: scale3d(0.97, 0.97, 0.97); transform: scale3d(0.97, 0.97, 0.97); } to { opacity: 1; -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } .animate__bounceIn { -webkit-animation-duration: calc(1s * 0.75); animation-duration: calc(1s * 0.75); -webkit-animation-duration: calc(var(--animate-duration) * 0.75); animation-duration: calc(var(--animate-duration) * 0.75); -webkit-animation-name: bounceIn; animation-name: bounceIn; } @-webkit-keyframes bounceInDown { from, 60%, 75%, 90%, to { -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 0% { opacity: 0; -webkit-transform: translate3d(0, -3000px, 0) scaleY(3); transform: translate3d(0, -3000px, 0) scaleY(3); } 60% { opacity: 1; -webkit-transform: translate3d(0, 25px, 0) scaleY(0.9); transform: translate3d(0, 25px, 0) scaleY(0.9); } 75% { -webkit-transform: translate3d(0, -10px, 0) scaleY(0.95); transform: translate3d(0, -10px, 0) scaleY(0.95); } 90% { -webkit-transform: translate3d(0, 5px, 0) scaleY(0.985); transform: translate3d(0, 5px, 0) scaleY(0.985); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes bounceInDown { from, 60%, 75%, 90%, to { -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 0% { opacity: 0; -webkit-transform: translate3d(0, -3000px, 0) scaleY(3); transform: translate3d(0, -3000px, 0) scaleY(3); } 60% { opacity: 1; -webkit-transform: translate3d(0, 25px, 0) scaleY(0.9); transform: translate3d(0, 25px, 0) scaleY(0.9); } 75% { -webkit-transform: translate3d(0, -10px, 0) scaleY(0.95); transform: translate3d(0, -10px, 0) scaleY(0.95); } 90% { -webkit-transform: translate3d(0, 5px, 0) scaleY(0.985); transform: translate3d(0, 5px, 0) scaleY(0.985); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__bounceInDown { -webkit-animation-name: bounceInDown; animation-name: bounceInDown; } @-webkit-keyframes bounceInLeft { from, 60%, 75%, 90%, to { -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 0% { opacity: 0; -webkit-transform: translate3d(-3000px, 0, 0) scaleX(3); transform: translate3d(-3000px, 0, 0) scaleX(3); } 60% { opacity: 1; -webkit-transform: translate3d(25px, 0, 0) scaleX(1); transform: translate3d(25px, 0, 0) scaleX(1); } 75% { -webkit-transform: translate3d(-10px, 0, 0) scaleX(0.98); transform: translate3d(-10px, 0, 0) scaleX(0.98); } 90% { -webkit-transform: translate3d(5px, 0, 0) scaleX(0.995); transform: translate3d(5px, 0, 0) scaleX(0.995); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes bounceInLeft { from, 60%, 75%, 90%, to { -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 0% { opacity: 0; -webkit-transform: translate3d(-3000px, 0, 0) scaleX(3); transform: translate3d(-3000px, 0, 0) scaleX(3); } 60% { opacity: 1; -webkit-transform: translate3d(25px, 0, 0) scaleX(1); transform: translate3d(25px, 0, 0) scaleX(1); } 75% { -webkit-transform: translate3d(-10px, 0, 0) scaleX(0.98); transform: translate3d(-10px, 0, 0) scaleX(0.98); } 90% { -webkit-transform: translate3d(5px, 0, 0) scaleX(0.995); transform: translate3d(5px, 0, 0) scaleX(0.995); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__bounceInLeft { -webkit-animation-name: bounceInLeft; animation-name: bounceInLeft; } @-webkit-keyframes bounceInRight { from, 60%, 75%, 90%, to { -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } from { opacity: 0; -webkit-transform: translate3d(3000px, 0, 0) scaleX(3); transform: translate3d(3000px, 0, 0) scaleX(3); } 60% { opacity: 1; -webkit-transform: translate3d(-25px, 0, 0) scaleX(1); transform: translate3d(-25px, 0, 0) scaleX(1); } 75% { -webkit-transform: translate3d(10px, 0, 0) scaleX(0.98); transform: translate3d(10px, 0, 0) scaleX(0.98); } 90% { -webkit-transform: translate3d(-5px, 0, 0) scaleX(0.995); transform: translate3d(-5px, 0, 0) scaleX(0.995); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes bounceInRight { from, 60%, 75%, 90%, to { -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } from { opacity: 0; -webkit-transform: translate3d(3000px, 0, 0) scaleX(3); transform: translate3d(3000px, 0, 0) scaleX(3); } 60% { opacity: 1; -webkit-transform: translate3d(-25px, 0, 0) scaleX(1); transform: translate3d(-25px, 0, 0) scaleX(1); } 75% { -webkit-transform: translate3d(10px, 0, 0) scaleX(0.98); transform: translate3d(10px, 0, 0) scaleX(0.98); } 90% { -webkit-transform: translate3d(-5px, 0, 0) scaleX(0.995); transform: translate3d(-5px, 0, 0) scaleX(0.995); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__bounceInRight { -webkit-animation-name: bounceInRight; animation-name: bounceInRight; } @-webkit-keyframes bounceInUp { from, 60%, 75%, 90%, to { -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } from { opacity: 0; -webkit-transform: translate3d(0, 3000px, 0) scaleY(5); transform: translate3d(0, 3000px, 0) scaleY(5); } 60% { opacity: 1; -webkit-transform: translate3d(0, -20px, 0) scaleY(0.9); transform: translate3d(0, -20px, 0) scaleY(0.9); } 75% { -webkit-transform: translate3d(0, 10px, 0) scaleY(0.95); transform: translate3d(0, 10px, 0) scaleY(0.95); } 90% { -webkit-transform: translate3d(0, -5px, 0) scaleY(0.985); transform: translate3d(0, -5px, 0) scaleY(0.985); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes bounceInUp { from, 60%, 75%, 90%, to { -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } from { opacity: 0; -webkit-transform: translate3d(0, 3000px, 0) scaleY(5); transform: translate3d(0, 3000px, 0) scaleY(5); } 60% { opacity: 1; -webkit-transform: translate3d(0, -20px, 0) scaleY(0.9); transform: translate3d(0, -20px, 0) scaleY(0.9); } 75% { -webkit-transform: translate3d(0, 10px, 0) scaleY(0.95); transform: translate3d(0, 10px, 0) scaleY(0.95); } 90% { -webkit-transform: translate3d(0, -5px, 0) scaleY(0.985); transform: translate3d(0, -5px, 0) scaleY(0.985); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__bounceInUp { -webkit-animation-name: bounceInUp; animation-name: bounceInUp; } /* Bouncing exits */ @-webkit-keyframes bounceOut { 20% { -webkit-transform: scale3d(0.9, 0.9, 0.9); transform: scale3d(0.9, 0.9, 0.9); } 50%, 55% { opacity: 1; -webkit-transform: scale3d(1.1, 1.1, 1.1); transform: scale3d(1.1, 1.1, 1.1); } to { opacity: 0; -webkit-transform: scale3d(0.3, 0.3, 0.3); transform: scale3d(0.3, 0.3, 0.3); } } @keyframes bounceOut { 20% { -webkit-transform: scale3d(0.9, 0.9, 0.9); transform: scale3d(0.9, 0.9, 0.9); } 50%, 55% { opacity: 1; -webkit-transform: scale3d(1.1, 1.1, 1.1); transform: scale3d(1.1, 1.1, 1.1); } to { opacity: 0; -webkit-transform: scale3d(0.3, 0.3, 0.3); transform: scale3d(0.3, 0.3, 0.3); } } .animate__bounceOut { -webkit-animation-duration: calc(1s * 0.75); animation-duration: calc(1s * 0.75); -webkit-animation-duration: calc(var(--animate-duration) * 0.75); animation-duration: calc(var(--animate-duration) * 0.75); -webkit-animation-name: bounceOut; animation-name: bounceOut; } @-webkit-keyframes bounceOutDown { 20% { -webkit-transform: translate3d(0, 10px, 0) scaleY(0.985); transform: translate3d(0, 10px, 0) scaleY(0.985); } 40%, 45% { opacity: 1; -webkit-transform: translate3d(0, -20px, 0) scaleY(0.9); transform: translate3d(0, -20px, 0) scaleY(0.9); } to { opacity: 0; -webkit-transform: translate3d(0, 2000px, 0) scaleY(3); transform: translate3d(0, 2000px, 0) scaleY(3); } } @keyframes bounceOutDown { 20% { -webkit-transform: translate3d(0, 10px, 0) scaleY(0.985); transform: translate3d(0, 10px, 0) scaleY(0.985); } 40%, 45% { opacity: 1; -webkit-transform: translate3d(0, -20px, 0) scaleY(0.9); transform: translate3d(0, -20px, 0) scaleY(0.9); } to { opacity: 0; -webkit-transform: translate3d(0, 2000px, 0) scaleY(3); transform: translate3d(0, 2000px, 0) scaleY(3); } } .animate__bounceOutDown { -webkit-animation-name: bounceOutDown; animation-name: bounceOutDown; } @-webkit-keyframes bounceOutLeft { 20% { opacity: 1; -webkit-transform: translate3d(20px, 0, 0) scaleX(0.9); transform: translate3d(20px, 0, 0) scaleX(0.9); } to { opacity: 0; -webkit-transform: translate3d(-2000px, 0, 0) scaleX(2); transform: translate3d(-2000px, 0, 0) scaleX(2); } } @keyframes bounceOutLeft { 20% { opacity: 1; -webkit-transform: translate3d(20px, 0, 0) scaleX(0.9); transform: translate3d(20px, 0, 0) scaleX(0.9); } to { opacity: 0; -webkit-transform: translate3d(-2000px, 0, 0) scaleX(2); transform: translate3d(-2000px, 0, 0) scaleX(2); } } .animate__bounceOutLeft { -webkit-animation-name: bounceOutLeft; animation-name: bounceOutLeft; } @-webkit-keyframes bounceOutRight { 20% { opacity: 1; -webkit-transform: translate3d(-20px, 0, 0) scaleX(0.9); transform: translate3d(-20px, 0, 0) scaleX(0.9); } to { opacity: 0; -webkit-transform: translate3d(2000px, 0, 0) scaleX(2); transform: translate3d(2000px, 0, 0) scaleX(2); } } @keyframes bounceOutRight { 20% { opacity: 1; -webkit-transform: translate3d(-20px, 0, 0) scaleX(0.9); transform: translate3d(-20px, 0, 0) scaleX(0.9); } to { opacity: 0; -webkit-transform: translate3d(2000px, 0, 0) scaleX(2); transform: translate3d(2000px, 0, 0) scaleX(2); } } .animate__bounceOutRight { -webkit-animation-name: bounceOutRight; animation-name: bounceOutRight; } @-webkit-keyframes bounceOutUp { 20% { -webkit-transform: translate3d(0, -10px, 0) scaleY(0.985); transform: translate3d(0, -10px, 0) scaleY(0.985); } 40%, 45% { opacity: 1; -webkit-transform: translate3d(0, 20px, 0) scaleY(0.9); transform: translate3d(0, 20px, 0) scaleY(0.9); } to { opacity: 0; -webkit-transform: translate3d(0, -2000px, 0) scaleY(3); transform: translate3d(0, -2000px, 0) scaleY(3); } } @keyframes bounceOutUp { 20% { -webkit-transform: translate3d(0, -10px, 0) scaleY(0.985); transform: translate3d(0, -10px, 0) scaleY(0.985); } 40%, 45% { opacity: 1; -webkit-transform: translate3d(0, 20px, 0) scaleY(0.9); transform: translate3d(0, 20px, 0) scaleY(0.9); } to { opacity: 0; -webkit-transform: translate3d(0, -2000px, 0) scaleY(3); transform: translate3d(0, -2000px, 0) scaleY(3); } } .animate__bounceOutUp { -webkit-animation-name: bounceOutUp; animation-name: bounceOutUp; } /* Fading entrances */ @-webkit-keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .animate__fadeIn { -webkit-animation-name: fadeIn; animation-name: fadeIn; } @-webkit-keyframes fadeInDown { from { opacity: 0; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes fadeInDown { from { opacity: 0; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__fadeInDown { -webkit-animation-name: fadeInDown; animation-name: fadeInDown; } @-webkit-keyframes fadeInDownBig { from { opacity: 0; -webkit-transform: translate3d(0, -2000px, 0); transform: translate3d(0, -2000px, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes fadeInDownBig { from { opacity: 0; -webkit-transform: translate3d(0, -2000px, 0); transform: translate3d(0, -2000px, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__fadeInDownBig { -webkit-animation-name: fadeInDownBig; animation-name: fadeInDownBig; } @-webkit-keyframes fadeInLeft { from { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes fadeInLeft { from { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__fadeInLeft { -webkit-animation-name: fadeInLeft; animation-name: fadeInLeft; } @-webkit-keyframes fadeInLeftBig { from { opacity: 0; -webkit-transform: translate3d(-2000px, 0, 0); transform: translate3d(-2000px, 0, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes fadeInLeftBig { from { opacity: 0; -webkit-transform: translate3d(-2000px, 0, 0); transform: translate3d(-2000px, 0, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__fadeInLeftBig { -webkit-animation-name: fadeInLeftBig; animation-name: fadeInLeftBig; } @-webkit-keyframes fadeInRight { from { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes fadeInRight { from { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__fadeInRight { -webkit-animation-name: fadeInRight; animation-name: fadeInRight; } @-webkit-keyframes fadeInRightBig { from { opacity: 0; -webkit-transform: translate3d(2000px, 0, 0); transform: translate3d(2000px, 0, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes fadeInRightBig { from { opacity: 0; -webkit-transform: translate3d(2000px, 0, 0); transform: translate3d(2000px, 0, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__fadeInRightBig { -webkit-animation-name: fadeInRightBig; animation-name: fadeInRightBig; } @-webkit-keyframes fadeInUp { from { opacity: 0; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes fadeInUp { from { opacity: 0; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__fadeInUp { -webkit-animation-name: fadeInUp; animation-name: fadeInUp; } @-webkit-keyframes fadeInUpBig { from { opacity: 0; -webkit-transform: translate3d(0, 2000px, 0); transform: translate3d(0, 2000px, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes fadeInUpBig { from { opacity: 0; -webkit-transform: translate3d(0, 2000px, 0); transform: translate3d(0, 2000px, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__fadeInUpBig { -webkit-animation-name: fadeInUpBig; animation-name: fadeInUpBig; } @-webkit-keyframes fadeInTopLeft { from { opacity: 0; -webkit-transform: translate3d(-100%, -100%, 0); transform: translate3d(-100%, -100%, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes fadeInTopLeft { from { opacity: 0; -webkit-transform: translate3d(-100%, -100%, 0); transform: translate3d(-100%, -100%, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__fadeInTopLeft { -webkit-animation-name: fadeInTopLeft; animation-name: fadeInTopLeft; } @-webkit-keyframes fadeInTopRight { from { opacity: 0; -webkit-transform: translate3d(100%, -100%, 0); transform: translate3d(100%, -100%, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes fadeInTopRight { from { opacity: 0; -webkit-transform: translate3d(100%, -100%, 0); transform: translate3d(100%, -100%, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__fadeInTopRight { -webkit-animation-name: fadeInTopRight; animation-name: fadeInTopRight; } @-webkit-keyframes fadeInBottomLeft { from { opacity: 0; -webkit-transform: translate3d(-100%, 100%, 0); transform: translate3d(-100%, 100%, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes fadeInBottomLeft { from { opacity: 0; -webkit-transform: translate3d(-100%, 100%, 0); transform: translate3d(-100%, 100%, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__fadeInBottomLeft { -webkit-animation-name: fadeInBottomLeft; animation-name: fadeInBottomLeft; } @-webkit-keyframes fadeInBottomRight { from { opacity: 0; -webkit-transform: translate3d(100%, 100%, 0); transform: translate3d(100%, 100%, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes fadeInBottomRight { from { opacity: 0; -webkit-transform: translate3d(100%, 100%, 0); transform: translate3d(100%, 100%, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__fadeInBottomRight { -webkit-animation-name: fadeInBottomRight; animation-name: fadeInBottomRight; } /* Fading exits */ @-webkit-keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } .animate__fadeOut { -webkit-animation-name: fadeOut; animation-name: fadeOut; } @-webkit-keyframes fadeOutDown { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } } @keyframes fadeOutDown { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } } .animate__fadeOutDown { -webkit-animation-name: fadeOutDown; animation-name: fadeOutDown; } @-webkit-keyframes fadeOutDownBig { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(0, 2000px, 0); transform: translate3d(0, 2000px, 0); } } @keyframes fadeOutDownBig { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(0, 2000px, 0); transform: translate3d(0, 2000px, 0); } } .animate__fadeOutDownBig { -webkit-animation-name: fadeOutDownBig; animation-name: fadeOutDownBig; } @-webkit-keyframes fadeOutLeft { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } } @keyframes fadeOutLeft { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } } .animate__fadeOutLeft { -webkit-animation-name: fadeOutLeft; animation-name: fadeOutLeft; } @-webkit-keyframes fadeOutLeftBig { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(-2000px, 0, 0); transform: translate3d(-2000px, 0, 0); } } @keyframes fadeOutLeftBig { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(-2000px, 0, 0); transform: translate3d(-2000px, 0, 0); } } .animate__fadeOutLeftBig { -webkit-animation-name: fadeOutLeftBig; animation-name: fadeOutLeftBig; } @-webkit-keyframes fadeOutRight { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } } @keyframes fadeOutRight { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } } .animate__fadeOutRight { -webkit-animation-name: fadeOutRight; animation-name: fadeOutRight; } @-webkit-keyframes fadeOutRightBig { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(2000px, 0, 0); transform: translate3d(2000px, 0, 0); } } @keyframes fadeOutRightBig { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(2000px, 0, 0); transform: translate3d(2000px, 0, 0); } } .animate__fadeOutRightBig { -webkit-animation-name: fadeOutRightBig; animation-name: fadeOutRightBig; } @-webkit-keyframes fadeOutUp { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } } @keyframes fadeOutUp { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } } .animate__fadeOutUp { -webkit-animation-name: fadeOutUp; animation-name: fadeOutUp; } @-webkit-keyframes fadeOutUpBig { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(0, -2000px, 0); transform: translate3d(0, -2000px, 0); } } @keyframes fadeOutUpBig { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(0, -2000px, 0); transform: translate3d(0, -2000px, 0); } } .animate__fadeOutUpBig { -webkit-animation-name: fadeOutUpBig; animation-name: fadeOutUpBig; } @-webkit-keyframes fadeOutTopLeft { from { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { opacity: 0; -webkit-transform: translate3d(-100%, -100%, 0); transform: translate3d(-100%, -100%, 0); } } @keyframes fadeOutTopLeft { from { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { opacity: 0; -webkit-transform: translate3d(-100%, -100%, 0); transform: translate3d(-100%, -100%, 0); } } .animate__fadeOutTopLeft { -webkit-animation-name: fadeOutTopLeft; animation-name: fadeOutTopLeft; } @-webkit-keyframes fadeOutTopRight { from { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { opacity: 0; -webkit-transform: translate3d(100%, -100%, 0); transform: translate3d(100%, -100%, 0); } } @keyframes fadeOutTopRight { from { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { opacity: 0; -webkit-transform: translate3d(100%, -100%, 0); transform: translate3d(100%, -100%, 0); } } .animate__fadeOutTopRight { -webkit-animation-name: fadeOutTopRight; animation-name: fadeOutTopRight; } @-webkit-keyframes fadeOutBottomRight { from { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { opacity: 0; -webkit-transform: translate3d(100%, 100%, 0); transform: translate3d(100%, 100%, 0); } } @keyframes fadeOutBottomRight { from { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { opacity: 0; -webkit-transform: translate3d(100%, 100%, 0); transform: translate3d(100%, 100%, 0); } } .animate__fadeOutBottomRight { -webkit-animation-name: fadeOutBottomRight; animation-name: fadeOutBottomRight; } @-webkit-keyframes fadeOutBottomLeft { from { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { opacity: 0; -webkit-transform: translate3d(-100%, 100%, 0); transform: translate3d(-100%, 100%, 0); } } @keyframes fadeOutBottomLeft { from { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { opacity: 0; -webkit-transform: translate3d(-100%, 100%, 0); transform: translate3d(-100%, 100%, 0); } } .animate__fadeOutBottomLeft { -webkit-animation-name: fadeOutBottomLeft; animation-name: fadeOutBottomLeft; } /* Flippers */ @-webkit-keyframes flip { from { -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, -360deg); transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, -360deg); -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } 40% { -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } 50% { -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 80% { -webkit-transform: perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg); transform: perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } to { -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg); transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } } @keyframes flip { from { -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, -360deg); transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, -360deg); -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } 40% { -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } 50% { -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 80% { -webkit-transform: perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg); transform: perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } to { -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg); transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } } .animate__animated.animate__flip { -webkit-backface-visibility: visible; backface-visibility: visible; -webkit-animation-name: flip; animation-name: flip; } @-webkit-keyframes flipInX { from { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); transform: perspective(400px) rotate3d(1, 0, 0, 90deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; opacity: 0; } 40% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); transform: perspective(400px) rotate3d(1, 0, 0, -20deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 60% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); transform: perspective(400px) rotate3d(1, 0, 0, 10deg); opacity: 1; } 80% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); transform: perspective(400px) rotate3d(1, 0, 0, -5deg); } to { -webkit-transform: perspective(400px); transform: perspective(400px); } } @keyframes flipInX { from { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); transform: perspective(400px) rotate3d(1, 0, 0, 90deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; opacity: 0; } 40% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); transform: perspective(400px) rotate3d(1, 0, 0, -20deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 60% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); transform: perspective(400px) rotate3d(1, 0, 0, 10deg); opacity: 1; } 80% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); transform: perspective(400px) rotate3d(1, 0, 0, -5deg); } to { -webkit-transform: perspective(400px); transform: perspective(400px); } } .animate__flipInX { -webkit-backface-visibility: visible !important; backface-visibility: visible !important; -webkit-animation-name: flipInX; animation-name: flipInX; } @-webkit-keyframes flipInY { from { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); transform: perspective(400px) rotate3d(0, 1, 0, 90deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; opacity: 0; } 40% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); transform: perspective(400px) rotate3d(0, 1, 0, -20deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 60% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); transform: perspective(400px) rotate3d(0, 1, 0, 10deg); opacity: 1; } 80% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); transform: perspective(400px) rotate3d(0, 1, 0, -5deg); } to { -webkit-transform: perspective(400px); transform: perspective(400px); } } @keyframes flipInY { from { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); transform: perspective(400px) rotate3d(0, 1, 0, 90deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; opacity: 0; } 40% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); transform: perspective(400px) rotate3d(0, 1, 0, -20deg); -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 60% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); transform: perspective(400px) rotate3d(0, 1, 0, 10deg); opacity: 1; } 80% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); transform: perspective(400px) rotate3d(0, 1, 0, -5deg); } to { -webkit-transform: perspective(400px); transform: perspective(400px); } } .animate__flipInY { -webkit-backface-visibility: visible !important; backface-visibility: visible !important; -webkit-animation-name: flipInY; animation-name: flipInY; } @-webkit-keyframes flipOutX { from { -webkit-transform: perspective(400px); transform: perspective(400px); } 30% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); transform: perspective(400px) rotate3d(1, 0, 0, -20deg); opacity: 1; } to { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); transform: perspective(400px) rotate3d(1, 0, 0, 90deg); opacity: 0; } } @keyframes flipOutX { from { -webkit-transform: perspective(400px); transform: perspective(400px); } 30% { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); transform: perspective(400px) rotate3d(1, 0, 0, -20deg); opacity: 1; } to { -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); transform: perspective(400px) rotate3d(1, 0, 0, 90deg); opacity: 0; } } .animate__flipOutX { -webkit-animation-duration: calc(1s * 0.75); animation-duration: calc(1s * 0.75); -webkit-animation-duration: calc(var(--animate-duration) * 0.75); animation-duration: calc(var(--animate-duration) * 0.75); -webkit-animation-name: flipOutX; animation-name: flipOutX; -webkit-backface-visibility: visible !important; backface-visibility: visible !important; } @-webkit-keyframes flipOutY { from { -webkit-transform: perspective(400px); transform: perspective(400px); } 30% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); transform: perspective(400px) rotate3d(0, 1, 0, -15deg); opacity: 1; } to { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); transform: perspective(400px) rotate3d(0, 1, 0, 90deg); opacity: 0; } } @keyframes flipOutY { from { -webkit-transform: perspective(400px); transform: perspective(400px); } 30% { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); transform: perspective(400px) rotate3d(0, 1, 0, -15deg); opacity: 1; } to { -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); transform: perspective(400px) rotate3d(0, 1, 0, 90deg); opacity: 0; } } .animate__flipOutY { -webkit-animation-duration: calc(1s * 0.75); animation-duration: calc(1s * 0.75); -webkit-animation-duration: calc(var(--animate-duration) * 0.75); animation-duration: calc(var(--animate-duration) * 0.75); -webkit-backface-visibility: visible !important; backface-visibility: visible !important; -webkit-animation-name: flipOutY; animation-name: flipOutY; } /* Lightspeed */ @-webkit-keyframes lightSpeedInRight { from { -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); transform: translate3d(100%, 0, 0) skewX(-30deg); opacity: 0; } 60% { -webkit-transform: skewX(20deg); transform: skewX(20deg); opacity: 1; } 80% { -webkit-transform: skewX(-5deg); transform: skewX(-5deg); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes lightSpeedInRight { from { -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); transform: translate3d(100%, 0, 0) skewX(-30deg); opacity: 0; } 60% { -webkit-transform: skewX(20deg); transform: skewX(20deg); opacity: 1; } 80% { -webkit-transform: skewX(-5deg); transform: skewX(-5deg); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__lightSpeedInRight { -webkit-animation-name: lightSpeedInRight; animation-name: lightSpeedInRight; -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } @-webkit-keyframes lightSpeedInLeft { from { -webkit-transform: translate3d(-100%, 0, 0) skewX(30deg); transform: translate3d(-100%, 0, 0) skewX(30deg); opacity: 0; } 60% { -webkit-transform: skewX(-20deg); transform: skewX(-20deg); opacity: 1; } 80% { -webkit-transform: skewX(5deg); transform: skewX(5deg); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes lightSpeedInLeft { from { -webkit-transform: translate3d(-100%, 0, 0) skewX(30deg); transform: translate3d(-100%, 0, 0) skewX(30deg); opacity: 0; } 60% { -webkit-transform: skewX(-20deg); transform: skewX(-20deg); opacity: 1; } 80% { -webkit-transform: skewX(5deg); transform: skewX(5deg); } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__lightSpeedInLeft { -webkit-animation-name: lightSpeedInLeft; animation-name: lightSpeedInLeft; -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } @-webkit-keyframes lightSpeedOutRight { from { opacity: 1; } to { -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); transform: translate3d(100%, 0, 0) skewX(30deg); opacity: 0; } } @keyframes lightSpeedOutRight { from { opacity: 1; } to { -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); transform: translate3d(100%, 0, 0) skewX(30deg); opacity: 0; } } .animate__lightSpeedOutRight { -webkit-animation-name: lightSpeedOutRight; animation-name: lightSpeedOutRight; -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } @-webkit-keyframes lightSpeedOutLeft { from { opacity: 1; } to { -webkit-transform: translate3d(-100%, 0, 0) skewX(-30deg); transform: translate3d(-100%, 0, 0) skewX(-30deg); opacity: 0; } } @keyframes lightSpeedOutLeft { from { opacity: 1; } to { -webkit-transform: translate3d(-100%, 0, 0) skewX(-30deg); transform: translate3d(-100%, 0, 0) skewX(-30deg); opacity: 0; } } .animate__lightSpeedOutLeft { -webkit-animation-name: lightSpeedOutLeft; animation-name: lightSpeedOutLeft; -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } /* Rotating entrances */ @-webkit-keyframes rotateIn { from { -webkit-transform: rotate3d(0, 0, 1, -200deg); transform: rotate3d(0, 0, 1, -200deg); opacity: 0; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); opacity: 1; } } @keyframes rotateIn { from { -webkit-transform: rotate3d(0, 0, 1, -200deg); transform: rotate3d(0, 0, 1, -200deg); opacity: 0; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); opacity: 1; } } .animate__rotateIn { -webkit-animation-name: rotateIn; animation-name: rotateIn; -webkit-transform-origin: center; transform-origin: center; } @-webkit-keyframes rotateInDownLeft { from { -webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg); opacity: 0; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); opacity: 1; } } @keyframes rotateInDownLeft { from { -webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg); opacity: 0; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); opacity: 1; } } .animate__rotateInDownLeft { -webkit-animation-name: rotateInDownLeft; animation-name: rotateInDownLeft; -webkit-transform-origin: left bottom; transform-origin: left bottom; } @-webkit-keyframes rotateInDownRight { from { -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg); opacity: 0; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); opacity: 1; } } @keyframes rotateInDownRight { from { -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg); opacity: 0; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); opacity: 1; } } .animate__rotateInDownRight { -webkit-animation-name: rotateInDownRight; animation-name: rotateInDownRight; -webkit-transform-origin: right bottom; transform-origin: right bottom; } @-webkit-keyframes rotateInUpLeft { from { -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg); opacity: 0; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); opacity: 1; } } @keyframes rotateInUpLeft { from { -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg); opacity: 0; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); opacity: 1; } } .animate__rotateInUpLeft { -webkit-animation-name: rotateInUpLeft; animation-name: rotateInUpLeft; -webkit-transform-origin: left bottom; transform-origin: left bottom; } @-webkit-keyframes rotateInUpRight { from { -webkit-transform: rotate3d(0, 0, 1, -90deg); transform: rotate3d(0, 0, 1, -90deg); opacity: 0; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); opacity: 1; } } @keyframes rotateInUpRight { from { -webkit-transform: rotate3d(0, 0, 1, -90deg); transform: rotate3d(0, 0, 1, -90deg); opacity: 0; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); opacity: 1; } } .animate__rotateInUpRight { -webkit-animation-name: rotateInUpRight; animation-name: rotateInUpRight; -webkit-transform-origin: right bottom; transform-origin: right bottom; } /* Rotating exits */ @-webkit-keyframes rotateOut { from { opacity: 1; } to { -webkit-transform: rotate3d(0, 0, 1, 200deg); transform: rotate3d(0, 0, 1, 200deg); opacity: 0; } } @keyframes rotateOut { from { opacity: 1; } to { -webkit-transform: rotate3d(0, 0, 1, 200deg); transform: rotate3d(0, 0, 1, 200deg); opacity: 0; } } .animate__rotateOut { -webkit-animation-name: rotateOut; animation-name: rotateOut; -webkit-transform-origin: center; transform-origin: center; } @-webkit-keyframes rotateOutDownLeft { from { opacity: 1; } to { -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg); opacity: 0; } } @keyframes rotateOutDownLeft { from { opacity: 1; } to { -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg); opacity: 0; } } .animate__rotateOutDownLeft { -webkit-animation-name: rotateOutDownLeft; animation-name: rotateOutDownLeft; -webkit-transform-origin: left bottom; transform-origin: left bottom; } @-webkit-keyframes rotateOutDownRight { from { opacity: 1; } to { -webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg); opacity: 0; } } @keyframes rotateOutDownRight { from { opacity: 1; } to { -webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg); opacity: 0; } } .animate__rotateOutDownRight { -webkit-animation-name: rotateOutDownRight; animation-name: rotateOutDownRight; -webkit-transform-origin: right bottom; transform-origin: right bottom; } @-webkit-keyframes rotateOutUpLeft { from { opacity: 1; } to { -webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg); opacity: 0; } } @keyframes rotateOutUpLeft { from { opacity: 1; } to { -webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg); opacity: 0; } } .animate__rotateOutUpLeft { -webkit-animation-name: rotateOutUpLeft; animation-name: rotateOutUpLeft; -webkit-transform-origin: left bottom; transform-origin: left bottom; } @-webkit-keyframes rotateOutUpRight { from { opacity: 1; } to { -webkit-transform: rotate3d(0, 0, 1, 90deg); transform: rotate3d(0, 0, 1, 90deg); opacity: 0; } } @keyframes rotateOutUpRight { from { opacity: 1; } to { -webkit-transform: rotate3d(0, 0, 1, 90deg); transform: rotate3d(0, 0, 1, 90deg); opacity: 0; } } .animate__rotateOutUpRight { -webkit-animation-name: rotateOutUpRight; animation-name: rotateOutUpRight; -webkit-transform-origin: right bottom; transform-origin: right bottom; } /* Specials */ @-webkit-keyframes hinge { 0% { -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; } 20%, 60% { -webkit-transform: rotate3d(0, 0, 1, 80deg); transform: rotate3d(0, 0, 1, 80deg); -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; } 40%, 80% { -webkit-transform: rotate3d(0, 0, 1, 60deg); transform: rotate3d(0, 0, 1, 60deg); -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; opacity: 1; } to { -webkit-transform: translate3d(0, 700px, 0); transform: translate3d(0, 700px, 0); opacity: 0; } } @keyframes hinge { 0% { -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; } 20%, 60% { -webkit-transform: rotate3d(0, 0, 1, 80deg); transform: rotate3d(0, 0, 1, 80deg); -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; } 40%, 80% { -webkit-transform: rotate3d(0, 0, 1, 60deg); transform: rotate3d(0, 0, 1, 60deg); -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; opacity: 1; } to { -webkit-transform: translate3d(0, 700px, 0); transform: translate3d(0, 700px, 0); opacity: 0; } } .animate__hinge { -webkit-animation-duration: calc(1s * 2); animation-duration: calc(1s * 2); -webkit-animation-duration: calc(var(--animate-duration) * 2); animation-duration: calc(var(--animate-duration) * 2); -webkit-animation-name: hinge; animation-name: hinge; -webkit-transform-origin: top left; transform-origin: top left; } @-webkit-keyframes jackInTheBox { from { opacity: 0; -webkit-transform: scale(0.1) rotate(30deg); transform: scale(0.1) rotate(30deg); -webkit-transform-origin: center bottom; transform-origin: center bottom; } 50% { -webkit-transform: rotate(-10deg); transform: rotate(-10deg); } 70% { -webkit-transform: rotate(3deg); transform: rotate(3deg); } to { opacity: 1; -webkit-transform: scale(1); transform: scale(1); } } @keyframes jackInTheBox { from { opacity: 0; -webkit-transform: scale(0.1) rotate(30deg); transform: scale(0.1) rotate(30deg); -webkit-transform-origin: center bottom; transform-origin: center bottom; } 50% { -webkit-transform: rotate(-10deg); transform: rotate(-10deg); } 70% { -webkit-transform: rotate(3deg); transform: rotate(3deg); } to { opacity: 1; -webkit-transform: scale(1); transform: scale(1); } } .animate__jackInTheBox { -webkit-animation-name: jackInTheBox; animation-name: jackInTheBox; } /* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ @-webkit-keyframes rollIn { from { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes rollIn { from { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__rollIn { -webkit-animation-name: rollIn; animation-name: rollIn; } /* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ @-webkit-keyframes rollOut { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); } } @keyframes rollOut { from { opacity: 1; } to { opacity: 0; -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); } } .animate__rollOut { -webkit-animation-name: rollOut; animation-name: rollOut; } /* Zooming entrances */ @-webkit-keyframes zoomIn { from { opacity: 0; -webkit-transform: scale3d(0.3, 0.3, 0.3); transform: scale3d(0.3, 0.3, 0.3); } 50% { opacity: 1; } } @keyframes zoomIn { from { opacity: 0; -webkit-transform: scale3d(0.3, 0.3, 0.3); transform: scale3d(0.3, 0.3, 0.3); } 50% { opacity: 1; } } .animate__zoomIn { -webkit-animation-name: zoomIn; animation-name: zoomIn; } @-webkit-keyframes zoomInDown { from { opacity: 0; -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0); transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0); -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } 60% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); } } @keyframes zoomInDown { from { opacity: 0; -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0); transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0); -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } 60% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); } } .animate__zoomInDown { -webkit-animation-name: zoomInDown; animation-name: zoomInDown; } @-webkit-keyframes zoomInLeft { from { opacity: 0; -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0); transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } 60% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); } } @keyframes zoomInLeft { from { opacity: 0; -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0); transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } 60% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); } } .animate__zoomInLeft { -webkit-animation-name: zoomInLeft; animation-name: zoomInLeft; } @-webkit-keyframes zoomInRight { from { opacity: 0; -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0); transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } 60% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); } } @keyframes zoomInRight { from { opacity: 0; -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0); transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } 60% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); } } .animate__zoomInRight { -webkit-animation-name: zoomInRight; animation-name: zoomInRight; } @-webkit-keyframes zoomInUp { from { opacity: 0; -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0); transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0); -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } 60% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); } } @keyframes zoomInUp { from { opacity: 0; -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0); transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0); -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } 60% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); } } .animate__zoomInUp { -webkit-animation-name: zoomInUp; animation-name: zoomInUp; } /* Zooming exits */ @-webkit-keyframes zoomOut { from { opacity: 1; } 50% { opacity: 0; -webkit-transform: scale3d(0.3, 0.3, 0.3); transform: scale3d(0.3, 0.3, 0.3); } to { opacity: 0; } } @keyframes zoomOut { from { opacity: 1; } 50% { opacity: 0; -webkit-transform: scale3d(0.3, 0.3, 0.3); transform: scale3d(0.3, 0.3, 0.3); } to { opacity: 0; } } .animate__zoomOut { -webkit-animation-name: zoomOut; animation-name: zoomOut; } @-webkit-keyframes zoomOutDown { 40% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } to { opacity: 0; -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0); transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); } } @keyframes zoomOutDown { 40% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } to { opacity: 0; -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0); transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); } } .animate__zoomOutDown { -webkit-animation-name: zoomOutDown; animation-name: zoomOutDown; -webkit-transform-origin: center bottom; transform-origin: center bottom; } @-webkit-keyframes zoomOutLeft { 40% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0); } to { opacity: 0; -webkit-transform: scale(0.1) translate3d(-2000px, 0, 0); transform: scale(0.1) translate3d(-2000px, 0, 0); } } @keyframes zoomOutLeft { 40% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0); } to { opacity: 0; -webkit-transform: scale(0.1) translate3d(-2000px, 0, 0); transform: scale(0.1) translate3d(-2000px, 0, 0); } } .animate__zoomOutLeft { -webkit-animation-name: zoomOutLeft; animation-name: zoomOutLeft; -webkit-transform-origin: left center; transform-origin: left center; } @-webkit-keyframes zoomOutRight { 40% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0); } to { opacity: 0; -webkit-transform: scale(0.1) translate3d(2000px, 0, 0); transform: scale(0.1) translate3d(2000px, 0, 0); } } @keyframes zoomOutRight { 40% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0); } to { opacity: 0; -webkit-transform: scale(0.1) translate3d(2000px, 0, 0); transform: scale(0.1) translate3d(2000px, 0, 0); } } .animate__zoomOutRight { -webkit-animation-name: zoomOutRight; animation-name: zoomOutRight; -webkit-transform-origin: right center; transform-origin: right center; } @-webkit-keyframes zoomOutUp { 40% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } to { opacity: 0; -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0); transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); } } @keyframes zoomOutUp { 40% { opacity: 1; -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } to { opacity: 0; -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0); transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0); -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); } } .animate__zoomOutUp { -webkit-animation-name: zoomOutUp; animation-name: zoomOutUp; -webkit-transform-origin: center bottom; transform-origin: center bottom; } /* Sliding entrances */ @-webkit-keyframes slideInDown { from { -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); visibility: visible; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes slideInDown { from { -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); visibility: visible; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__slideInDown { -webkit-animation-name: slideInDown; animation-name: slideInDown; } @-webkit-keyframes slideInLeft { from { -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); visibility: visible; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes slideInLeft { from { -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); visibility: visible; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__slideInLeft { -webkit-animation-name: slideInLeft; animation-name: slideInLeft; } @-webkit-keyframes slideInRight { from { -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); visibility: visible; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes slideInRight { from { -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); visibility: visible; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__slideInRight { -webkit-animation-name: slideInRight; animation-name: slideInRight; } @-webkit-keyframes slideInUp { from { -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); visibility: visible; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes slideInUp { from { -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); visibility: visible; } to { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate__slideInUp { -webkit-animation-name: slideInUp; animation-name: slideInUp; } /* Sliding exits */ @-webkit-keyframes slideOutDown { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { visibility: hidden; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } } @keyframes slideOutDown { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { visibility: hidden; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } } .animate__slideOutDown { -webkit-animation-name: slideOutDown; animation-name: slideOutDown; } @-webkit-keyframes slideOutLeft { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { visibility: hidden; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } } @keyframes slideOutLeft { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { visibility: hidden; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } } .animate__slideOutLeft { -webkit-animation-name: slideOutLeft; animation-name: slideOutLeft; } @-webkit-keyframes slideOutRight { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { visibility: hidden; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } } @keyframes slideOutRight { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { visibility: hidden; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } } .animate__slideOutRight { -webkit-animation-name: slideOutRight; animation-name: slideOutRight; } @-webkit-keyframes slideOutUp { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { visibility: hidden; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } } @keyframes slideOutUp { from { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } to { visibility: hidden; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } } .animate__slideOutUp { -webkit-animation-name: slideOutUp; animation-name: slideOutUp; } ================================================ FILE: examples/tailwind-and-animate/package.json ================================================ { "name": "@paperclip-ui/tailwind-and-bootstrap", "private": true, "version": "18.5.6", "description": "", "main": "index.js", "directories": { "lib": "lib" }, "scripts": { "start": "yarn build && concurrently \"yarn build:watch\" \"yarn paperclip designer --canvas-file=./src/hello-paperclip.pc\"", "build": "yarn build:tailwind && yarn build:pc", "build:watch": "concurrently \"yarn build:pc --watch\" \"yarn build:tailwind --watch\"", "build:pc": "yarn paperclip build", "build:tailwind": "yarn tailwindcss -i src/tailwind.scss -o css-modules/tailwind.css" }, "author": "", "license": "ISC", "devDependencies": { "@paperclip-ui/cli": "^18.5.6", "@paperclip-ui/core": "^18.5.6", "concurrently": "^7.0.0", "postcss": "^8.4.5", "tailwindcss": "^3.0.12" } } ================================================ FILE: examples/tailwind-and-animate/paperclip.config.json ================================================ { "compilerOptions": [ { "outDir": "./lib" } ], "srcDir": "./src", "moduleDirs": ["css-modules"] } ================================================ FILE: examples/tailwind-and-animate/sandbox.config.json ================================================ { "template": "node", "view": "browser" } ================================================ FILE: examples/tailwind-and-animate/src/hello-paperclip.pc ================================================

    Register

    ================================================ FILE: examples/tailwind-and-animate/src/tailwind.scss ================================================ @tailwind base; @tailwind components; @tailwind utilities; ================================================ FILE: examples/tailwind-and-animate/tailwind.config.js ================================================ module.exports = { content: ["./src/**/*.pc"], theme: { extend: {} }, plugins: [] }; ================================================ FILE: examples/tailwind-and-bootstrap-example/.gitignore ================================================ node_modules tailwind.css ================================================ FILE: examples/tailwind-and-bootstrap-example/css-modules/bootstrap/bootstrap-grid.css ================================================ /*! * Bootstrap Grid v5.1.3 (https://getbootstrap.com/) * Copyright 2011-2021 The Bootstrap Authors * Copyright 2011-2021 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ :root { --bs-blue: #0d6efd; --bs-indigo: #6610f2; --bs-purple: #6f42c1; --bs-pink: #d63384; --bs-red: #dc3545; --bs-orange: #fd7e14; --bs-yellow: #ffc107; --bs-green: #198754; --bs-teal: #20c997; --bs-cyan: #0dcaf0; --bs-white: #fff; --bs-gray: #6c757d; --bs-gray-dark: #343a40; --bs-gray-100: #f8f9fa; --bs-gray-200: #e9ecef; --bs-gray-300: #dee2e6; --bs-gray-400: #ced4da; --bs-gray-500: #adb5bd; --bs-gray-600: #6c757d; --bs-gray-700: #495057; --bs-gray-800: #343a40; --bs-gray-900: #212529; --bs-primary: #0d6efd; --bs-secondary: #6c757d; --bs-success: #198754; --bs-info: #0dcaf0; --bs-warning: #ffc107; --bs-danger: #dc3545; --bs-light: #f8f9fa; --bs-dark: #212529; --bs-primary-rgb: 13, 110, 253; --bs-secondary-rgb: 108, 117, 125; --bs-success-rgb: 25, 135, 84; --bs-info-rgb: 13, 202, 240; --bs-warning-rgb: 255, 193, 7; --bs-danger-rgb: 220, 53, 69; --bs-light-rgb: 248, 249, 250; --bs-dark-rgb: 33, 37, 41; --bs-white-rgb: 255, 255, 255; --bs-black-rgb: 0, 0, 0; --bs-body-color-rgb: 33, 37, 41; --bs-body-bg-rgb: 255, 255, 255; --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --bs-gradient: linear-gradient( 180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0) ); --bs-body-font-family: var(--bs-font-sans-serif); --bs-body-font-size: 1rem; --bs-body-font-weight: 400; --bs-body-line-height: 1.5; --bs-body-color: #212529; --bs-body-bg: #fff; } .container, .container-fluid, .container-xxl, .container-xl, .container-lg, .container-md, .container-sm { width: 100%; padding-right: var(--bs-gutter-x, 0.75rem); padding-left: var(--bs-gutter-x, 0.75rem); margin-right: auto; margin-left: auto; } @media (min-width: 576px) { .container-sm, .container { max-width: 540px; } } @media (min-width: 768px) { .container-md, .container-sm, .container { max-width: 720px; } } @media (min-width: 992px) { .container-lg, .container-md, .container-sm, .container { max-width: 960px; } } @media (min-width: 1200px) { .container-xl, .container-lg, .container-md, .container-sm, .container { max-width: 1140px; } } @media (min-width: 1400px) { .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { max-width: 1320px; } } .row { --bs-gutter-x: 1.5rem; --bs-gutter-y: 0; display: flex; flex-wrap: wrap; margin-top: calc(-1 * var(--bs-gutter-y)); margin-right: calc(-0.5 * var(--bs-gutter-x)); margin-left: calc(-0.5 * var(--bs-gutter-x)); } .row > * { box-sizing: border-box; flex-shrink: 0; width: 100%; max-width: 100%; padding-right: calc(var(--bs-gutter-x) * 0.5); padding-left: calc(var(--bs-gutter-x) * 0.5); margin-top: var(--bs-gutter-y); } .col { flex: 1 0 0%; } .row-cols-auto > * { flex: 0 0 auto; width: auto; } .row-cols-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-auto { flex: 0 0 auto; width: auto; } .col-1 { flex: 0 0 auto; width: 8.33333333%; } .col-2 { flex: 0 0 auto; width: 16.66666667%; } .col-3 { flex: 0 0 auto; width: 25%; } .col-4 { flex: 0 0 auto; width: 33.33333333%; } .col-5 { flex: 0 0 auto; width: 41.66666667%; } .col-6 { flex: 0 0 auto; width: 50%; } .col-7 { flex: 0 0 auto; width: 58.33333333%; } .col-8 { flex: 0 0 auto; width: 66.66666667%; } .col-9 { flex: 0 0 auto; width: 75%; } .col-10 { flex: 0 0 auto; width: 83.33333333%; } .col-11 { flex: 0 0 auto; width: 91.66666667%; } .col-12 { flex: 0 0 auto; width: 100%; } .offset-1 { margin-left: 8.33333333%; } .offset-2 { margin-left: 16.66666667%; } .offset-3 { margin-left: 25%; } .offset-4 { margin-left: 33.33333333%; } .offset-5 { margin-left: 41.66666667%; } .offset-6 { margin-left: 50%; } .offset-7 { margin-left: 58.33333333%; } .offset-8 { margin-left: 66.66666667%; } .offset-9 { margin-left: 75%; } .offset-10 { margin-left: 83.33333333%; } .offset-11 { margin-left: 91.66666667%; } .g-0, .gx-0 { --bs-gutter-x: 0; } .g-0, .gy-0 { --bs-gutter-y: 0; } .g-1, .gx-1 { --bs-gutter-x: 0.25rem; } .g-1, .gy-1 { --bs-gutter-y: 0.25rem; } .g-2, .gx-2 { --bs-gutter-x: 0.5rem; } .g-2, .gy-2 { --bs-gutter-y: 0.5rem; } .g-3, .gx-3 { --bs-gutter-x: 1rem; } .g-3, .gy-3 { --bs-gutter-y: 1rem; } .g-4, .gx-4 { --bs-gutter-x: 1.5rem; } .g-4, .gy-4 { --bs-gutter-y: 1.5rem; } .g-5, .gx-5 { --bs-gutter-x: 3rem; } .g-5, .gy-5 { --bs-gutter-y: 3rem; } @media (min-width: 576px) { .col-sm { flex: 1 0 0%; } .row-cols-sm-auto > * { flex: 0 0 auto; width: auto; } .row-cols-sm-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-sm-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-sm-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-sm-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-sm-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-sm-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-sm-auto { flex: 0 0 auto; width: auto; } .col-sm-1 { flex: 0 0 auto; width: 8.33333333%; } .col-sm-2 { flex: 0 0 auto; width: 16.66666667%; } .col-sm-3 { flex: 0 0 auto; width: 25%; } .col-sm-4 { flex: 0 0 auto; width: 33.33333333%; } .col-sm-5 { flex: 0 0 auto; width: 41.66666667%; } .col-sm-6 { flex: 0 0 auto; width: 50%; } .col-sm-7 { flex: 0 0 auto; width: 58.33333333%; } .col-sm-8 { flex: 0 0 auto; width: 66.66666667%; } .col-sm-9 { flex: 0 0 auto; width: 75%; } .col-sm-10 { flex: 0 0 auto; width: 83.33333333%; } .col-sm-11 { flex: 0 0 auto; width: 91.66666667%; } .col-sm-12 { flex: 0 0 auto; width: 100%; } .offset-sm-0 { margin-left: 0; } .offset-sm-1 { margin-left: 8.33333333%; } .offset-sm-2 { margin-left: 16.66666667%; } .offset-sm-3 { margin-left: 25%; } .offset-sm-4 { margin-left: 33.33333333%; } .offset-sm-5 { margin-left: 41.66666667%; } .offset-sm-6 { margin-left: 50%; } .offset-sm-7 { margin-left: 58.33333333%; } .offset-sm-8 { margin-left: 66.66666667%; } .offset-sm-9 { margin-left: 75%; } .offset-sm-10 { margin-left: 83.33333333%; } .offset-sm-11 { margin-left: 91.66666667%; } .g-sm-0, .gx-sm-0 { --bs-gutter-x: 0; } .g-sm-0, .gy-sm-0 { --bs-gutter-y: 0; } .g-sm-1, .gx-sm-1 { --bs-gutter-x: 0.25rem; } .g-sm-1, .gy-sm-1 { --bs-gutter-y: 0.25rem; } .g-sm-2, .gx-sm-2 { --bs-gutter-x: 0.5rem; } .g-sm-2, .gy-sm-2 { --bs-gutter-y: 0.5rem; } .g-sm-3, .gx-sm-3 { --bs-gutter-x: 1rem; } .g-sm-3, .gy-sm-3 { --bs-gutter-y: 1rem; } .g-sm-4, .gx-sm-4 { --bs-gutter-x: 1.5rem; } .g-sm-4, .gy-sm-4 { --bs-gutter-y: 1.5rem; } .g-sm-5, .gx-sm-5 { --bs-gutter-x: 3rem; } .g-sm-5, .gy-sm-5 { --bs-gutter-y: 3rem; } } @media (min-width: 768px) { .col-md { flex: 1 0 0%; } .row-cols-md-auto > * { flex: 0 0 auto; width: auto; } .row-cols-md-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-md-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-md-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-md-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-md-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-md-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-md-auto { flex: 0 0 auto; width: auto; } .col-md-1 { flex: 0 0 auto; width: 8.33333333%; } .col-md-2 { flex: 0 0 auto; width: 16.66666667%; } .col-md-3 { flex: 0 0 auto; width: 25%; } .col-md-4 { flex: 0 0 auto; width: 33.33333333%; } .col-md-5 { flex: 0 0 auto; width: 41.66666667%; } .col-md-6 { flex: 0 0 auto; width: 50%; } .col-md-7 { flex: 0 0 auto; width: 58.33333333%; } .col-md-8 { flex: 0 0 auto; width: 66.66666667%; } .col-md-9 { flex: 0 0 auto; width: 75%; } .col-md-10 { flex: 0 0 auto; width: 83.33333333%; } .col-md-11 { flex: 0 0 auto; width: 91.66666667%; } .col-md-12 { flex: 0 0 auto; width: 100%; } .offset-md-0 { margin-left: 0; } .offset-md-1 { margin-left: 8.33333333%; } .offset-md-2 { margin-left: 16.66666667%; } .offset-md-3 { margin-left: 25%; } .offset-md-4 { margin-left: 33.33333333%; } .offset-md-5 { margin-left: 41.66666667%; } .offset-md-6 { margin-left: 50%; } .offset-md-7 { margin-left: 58.33333333%; } .offset-md-8 { margin-left: 66.66666667%; } .offset-md-9 { margin-left: 75%; } .offset-md-10 { margin-left: 83.33333333%; } .offset-md-11 { margin-left: 91.66666667%; } .g-md-0, .gx-md-0 { --bs-gutter-x: 0; } .g-md-0, .gy-md-0 { --bs-gutter-y: 0; } .g-md-1, .gx-md-1 { --bs-gutter-x: 0.25rem; } .g-md-1, .gy-md-1 { --bs-gutter-y: 0.25rem; } .g-md-2, .gx-md-2 { --bs-gutter-x: 0.5rem; } .g-md-2, .gy-md-2 { --bs-gutter-y: 0.5rem; } .g-md-3, .gx-md-3 { --bs-gutter-x: 1rem; } .g-md-3, .gy-md-3 { --bs-gutter-y: 1rem; } .g-md-4, .gx-md-4 { --bs-gutter-x: 1.5rem; } .g-md-4, .gy-md-4 { --bs-gutter-y: 1.5rem; } .g-md-5, .gx-md-5 { --bs-gutter-x: 3rem; } .g-md-5, .gy-md-5 { --bs-gutter-y: 3rem; } } @media (min-width: 992px) { .col-lg { flex: 1 0 0%; } .row-cols-lg-auto > * { flex: 0 0 auto; width: auto; } .row-cols-lg-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-lg-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-lg-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-lg-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-lg-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-lg-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-lg-auto { flex: 0 0 auto; width: auto; } .col-lg-1 { flex: 0 0 auto; width: 8.33333333%; } .col-lg-2 { flex: 0 0 auto; width: 16.66666667%; } .col-lg-3 { flex: 0 0 auto; width: 25%; } .col-lg-4 { flex: 0 0 auto; width: 33.33333333%; } .col-lg-5 { flex: 0 0 auto; width: 41.66666667%; } .col-lg-6 { flex: 0 0 auto; width: 50%; } .col-lg-7 { flex: 0 0 auto; width: 58.33333333%; } .col-lg-8 { flex: 0 0 auto; width: 66.66666667%; } .col-lg-9 { flex: 0 0 auto; width: 75%; } .col-lg-10 { flex: 0 0 auto; width: 83.33333333%; } .col-lg-11 { flex: 0 0 auto; width: 91.66666667%; } .col-lg-12 { flex: 0 0 auto; width: 100%; } .offset-lg-0 { margin-left: 0; } .offset-lg-1 { margin-left: 8.33333333%; } .offset-lg-2 { margin-left: 16.66666667%; } .offset-lg-3 { margin-left: 25%; } .offset-lg-4 { margin-left: 33.33333333%; } .offset-lg-5 { margin-left: 41.66666667%; } .offset-lg-6 { margin-left: 50%; } .offset-lg-7 { margin-left: 58.33333333%; } .offset-lg-8 { margin-left: 66.66666667%; } .offset-lg-9 { margin-left: 75%; } .offset-lg-10 { margin-left: 83.33333333%; } .offset-lg-11 { margin-left: 91.66666667%; } .g-lg-0, .gx-lg-0 { --bs-gutter-x: 0; } .g-lg-0, .gy-lg-0 { --bs-gutter-y: 0; } .g-lg-1, .gx-lg-1 { --bs-gutter-x: 0.25rem; } .g-lg-1, .gy-lg-1 { --bs-gutter-y: 0.25rem; } .g-lg-2, .gx-lg-2 { --bs-gutter-x: 0.5rem; } .g-lg-2, .gy-lg-2 { --bs-gutter-y: 0.5rem; } .g-lg-3, .gx-lg-3 { --bs-gutter-x: 1rem; } .g-lg-3, .gy-lg-3 { --bs-gutter-y: 1rem; } .g-lg-4, .gx-lg-4 { --bs-gutter-x: 1.5rem; } .g-lg-4, .gy-lg-4 { --bs-gutter-y: 1.5rem; } .g-lg-5, .gx-lg-5 { --bs-gutter-x: 3rem; } .g-lg-5, .gy-lg-5 { --bs-gutter-y: 3rem; } } @media (min-width: 1200px) { .col-xl { flex: 1 0 0%; } .row-cols-xl-auto > * { flex: 0 0 auto; width: auto; } .row-cols-xl-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-xl-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-xl-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-xl-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-xl-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-xl-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-xl-auto { flex: 0 0 auto; width: auto; } .col-xl-1 { flex: 0 0 auto; width: 8.33333333%; } .col-xl-2 { flex: 0 0 auto; width: 16.66666667%; } .col-xl-3 { flex: 0 0 auto; width: 25%; } .col-xl-4 { flex: 0 0 auto; width: 33.33333333%; } .col-xl-5 { flex: 0 0 auto; width: 41.66666667%; } .col-xl-6 { flex: 0 0 auto; width: 50%; } .col-xl-7 { flex: 0 0 auto; width: 58.33333333%; } .col-xl-8 { flex: 0 0 auto; width: 66.66666667%; } .col-xl-9 { flex: 0 0 auto; width: 75%; } .col-xl-10 { flex: 0 0 auto; width: 83.33333333%; } .col-xl-11 { flex: 0 0 auto; width: 91.66666667%; } .col-xl-12 { flex: 0 0 auto; width: 100%; } .offset-xl-0 { margin-left: 0; } .offset-xl-1 { margin-left: 8.33333333%; } .offset-xl-2 { margin-left: 16.66666667%; } .offset-xl-3 { margin-left: 25%; } .offset-xl-4 { margin-left: 33.33333333%; } .offset-xl-5 { margin-left: 41.66666667%; } .offset-xl-6 { margin-left: 50%; } .offset-xl-7 { margin-left: 58.33333333%; } .offset-xl-8 { margin-left: 66.66666667%; } .offset-xl-9 { margin-left: 75%; } .offset-xl-10 { margin-left: 83.33333333%; } .offset-xl-11 { margin-left: 91.66666667%; } .g-xl-0, .gx-xl-0 { --bs-gutter-x: 0; } .g-xl-0, .gy-xl-0 { --bs-gutter-y: 0; } .g-xl-1, .gx-xl-1 { --bs-gutter-x: 0.25rem; } .g-xl-1, .gy-xl-1 { --bs-gutter-y: 0.25rem; } .g-xl-2, .gx-xl-2 { --bs-gutter-x: 0.5rem; } .g-xl-2, .gy-xl-2 { --bs-gutter-y: 0.5rem; } .g-xl-3, .gx-xl-3 { --bs-gutter-x: 1rem; } .g-xl-3, .gy-xl-3 { --bs-gutter-y: 1rem; } .g-xl-4, .gx-xl-4 { --bs-gutter-x: 1.5rem; } .g-xl-4, .gy-xl-4 { --bs-gutter-y: 1.5rem; } .g-xl-5, .gx-xl-5 { --bs-gutter-x: 3rem; } .g-xl-5, .gy-xl-5 { --bs-gutter-y: 3rem; } } @media (min-width: 1400px) { .col-xxl { flex: 1 0 0%; } .row-cols-xxl-auto > * { flex: 0 0 auto; width: auto; } .row-cols-xxl-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-xxl-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-xxl-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-xxl-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-xxl-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-xxl-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-xxl-auto { flex: 0 0 auto; width: auto; } .col-xxl-1 { flex: 0 0 auto; width: 8.33333333%; } .col-xxl-2 { flex: 0 0 auto; width: 16.66666667%; } .col-xxl-3 { flex: 0 0 auto; width: 25%; } .col-xxl-4 { flex: 0 0 auto; width: 33.33333333%; } .col-xxl-5 { flex: 0 0 auto; width: 41.66666667%; } .col-xxl-6 { flex: 0 0 auto; width: 50%; } .col-xxl-7 { flex: 0 0 auto; width: 58.33333333%; } .col-xxl-8 { flex: 0 0 auto; width: 66.66666667%; } .col-xxl-9 { flex: 0 0 auto; width: 75%; } .col-xxl-10 { flex: 0 0 auto; width: 83.33333333%; } .col-xxl-11 { flex: 0 0 auto; width: 91.66666667%; } .col-xxl-12 { flex: 0 0 auto; width: 100%; } .offset-xxl-0 { margin-left: 0; } .offset-xxl-1 { margin-left: 8.33333333%; } .offset-xxl-2 { margin-left: 16.66666667%; } .offset-xxl-3 { margin-left: 25%; } .offset-xxl-4 { margin-left: 33.33333333%; } .offset-xxl-5 { margin-left: 41.66666667%; } .offset-xxl-6 { margin-left: 50%; } .offset-xxl-7 { margin-left: 58.33333333%; } .offset-xxl-8 { margin-left: 66.66666667%; } .offset-xxl-9 { margin-left: 75%; } .offset-xxl-10 { margin-left: 83.33333333%; } .offset-xxl-11 { margin-left: 91.66666667%; } .g-xxl-0, .gx-xxl-0 { --bs-gutter-x: 0; } .g-xxl-0, .gy-xxl-0 { --bs-gutter-y: 0; } .g-xxl-1, .gx-xxl-1 { --bs-gutter-x: 0.25rem; } .g-xxl-1, .gy-xxl-1 { --bs-gutter-y: 0.25rem; } .g-xxl-2, .gx-xxl-2 { --bs-gutter-x: 0.5rem; } .g-xxl-2, .gy-xxl-2 { --bs-gutter-y: 0.5rem; } .g-xxl-3, .gx-xxl-3 { --bs-gutter-x: 1rem; } .g-xxl-3, .gy-xxl-3 { --bs-gutter-y: 1rem; } .g-xxl-4, .gx-xxl-4 { --bs-gutter-x: 1.5rem; } .g-xxl-4, .gy-xxl-4 { --bs-gutter-y: 1.5rem; } .g-xxl-5, .gx-xxl-5 { --bs-gutter-x: 3rem; } .g-xxl-5, .gy-xxl-5 { --bs-gutter-y: 3rem; } } .d-inline { display: inline !important; } .d-inline-block { display: inline-block !important; } .d-block { display: block !important; } .d-grid { display: grid !important; } .d-table { display: table !important; } .d-table-row { display: table-row !important; } .d-table-cell { display: table-cell !important; } .d-flex { display: flex !important; } .d-inline-flex { display: inline-flex !important; } .d-none { display: none !important; } .flex-fill { flex: 1 1 auto !important; } .flex-row { flex-direction: row !important; } .flex-column { flex-direction: column !important; } .flex-row-reverse { flex-direction: row-reverse !important; } .flex-column-reverse { flex-direction: column-reverse !important; } .flex-grow-0 { flex-grow: 0 !important; } .flex-grow-1 { flex-grow: 1 !important; } .flex-shrink-0 { flex-shrink: 0 !important; } .flex-shrink-1 { flex-shrink: 1 !important; } .flex-wrap { flex-wrap: wrap !important; } .flex-nowrap { flex-wrap: nowrap !important; } .flex-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-start { justify-content: flex-start !important; } .justify-content-end { justify-content: flex-end !important; } .justify-content-center { justify-content: center !important; } .justify-content-between { justify-content: space-between !important; } .justify-content-around { justify-content: space-around !important; } .justify-content-evenly { justify-content: space-evenly !important; } .align-items-start { align-items: flex-start !important; } .align-items-end { align-items: flex-end !important; } .align-items-center { align-items: center !important; } .align-items-baseline { align-items: baseline !important; } .align-items-stretch { align-items: stretch !important; } .align-content-start { align-content: flex-start !important; } .align-content-end { align-content: flex-end !important; } .align-content-center { align-content: center !important; } .align-content-between { align-content: space-between !important; } .align-content-around { align-content: space-around !important; } .align-content-stretch { align-content: stretch !important; } .align-self-auto { align-self: auto !important; } .align-self-start { align-self: flex-start !important; } .align-self-end { align-self: flex-end !important; } .align-self-center { align-self: center !important; } .align-self-baseline { align-self: baseline !important; } .align-self-stretch { align-self: stretch !important; } .order-first { order: -1 !important; } .order-0 { order: 0 !important; } .order-1 { order: 1 !important; } .order-2 { order: 2 !important; } .order-3 { order: 3 !important; } .order-4 { order: 4 !important; } .order-5 { order: 5 !important; } .order-last { order: 6 !important; } .m-0 { margin: 0 !important; } .m-1 { margin: 0.25rem !important; } .m-2 { margin: 0.5rem !important; } .m-3 { margin: 1rem !important; } .m-4 { margin: 1.5rem !important; } .m-5 { margin: 3rem !important; } .m-auto { margin: auto !important; } .mx-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-auto { margin-right: auto !important; margin-left: auto !important; } .my-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-0 { margin-top: 0 !important; } .mt-1 { margin-top: 0.25rem !important; } .mt-2 { margin-top: 0.5rem !important; } .mt-3 { margin-top: 1rem !important; } .mt-4 { margin-top: 1.5rem !important; } .mt-5 { margin-top: 3rem !important; } .mt-auto { margin-top: auto !important; } .me-0 { margin-right: 0 !important; } .me-1 { margin-right: 0.25rem !important; } .me-2 { margin-right: 0.5rem !important; } .me-3 { margin-right: 1rem !important; } .me-4 { margin-right: 1.5rem !important; } .me-5 { margin-right: 3rem !important; } .me-auto { margin-right: auto !important; } .mb-0 { margin-bottom: 0 !important; } .mb-1 { margin-bottom: 0.25rem !important; } .mb-2 { margin-bottom: 0.5rem !important; } .mb-3 { margin-bottom: 1rem !important; } .mb-4 { margin-bottom: 1.5rem !important; } .mb-5 { margin-bottom: 3rem !important; } .mb-auto { margin-bottom: auto !important; } .ms-0 { margin-left: 0 !important; } .ms-1 { margin-left: 0.25rem !important; } .ms-2 { margin-left: 0.5rem !important; } .ms-3 { margin-left: 1rem !important; } .ms-4 { margin-left: 1.5rem !important; } .ms-5 { margin-left: 3rem !important; } .ms-auto { margin-left: auto !important; } .p-0 { padding: 0 !important; } .p-1 { padding: 0.25rem !important; } .p-2 { padding: 0.5rem !important; } .p-3 { padding: 1rem !important; } .p-4 { padding: 1.5rem !important; } .p-5 { padding: 3rem !important; } .px-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-0 { padding-top: 0 !important; } .pt-1 { padding-top: 0.25rem !important; } .pt-2 { padding-top: 0.5rem !important; } .pt-3 { padding-top: 1rem !important; } .pt-4 { padding-top: 1.5rem !important; } .pt-5 { padding-top: 3rem !important; } .pe-0 { padding-right: 0 !important; } .pe-1 { padding-right: 0.25rem !important; } .pe-2 { padding-right: 0.5rem !important; } .pe-3 { padding-right: 1rem !important; } .pe-4 { padding-right: 1.5rem !important; } .pe-5 { padding-right: 3rem !important; } .pb-0 { padding-bottom: 0 !important; } .pb-1 { padding-bottom: 0.25rem !important; } .pb-2 { padding-bottom: 0.5rem !important; } .pb-3 { padding-bottom: 1rem !important; } .pb-4 { padding-bottom: 1.5rem !important; } .pb-5 { padding-bottom: 3rem !important; } .ps-0 { padding-left: 0 !important; } .ps-1 { padding-left: 0.25rem !important; } .ps-2 { padding-left: 0.5rem !important; } .ps-3 { padding-left: 1rem !important; } .ps-4 { padding-left: 1.5rem !important; } .ps-5 { padding-left: 3rem !important; } @media (min-width: 576px) { .d-sm-inline { display: inline !important; } .d-sm-inline-block { display: inline-block !important; } .d-sm-block { display: block !important; } .d-sm-grid { display: grid !important; } .d-sm-table { display: table !important; } .d-sm-table-row { display: table-row !important; } .d-sm-table-cell { display: table-cell !important; } .d-sm-flex { display: flex !important; } .d-sm-inline-flex { display: inline-flex !important; } .d-sm-none { display: none !important; } .flex-sm-fill { flex: 1 1 auto !important; } .flex-sm-row { flex-direction: row !important; } .flex-sm-column { flex-direction: column !important; } .flex-sm-row-reverse { flex-direction: row-reverse !important; } .flex-sm-column-reverse { flex-direction: column-reverse !important; } .flex-sm-grow-0 { flex-grow: 0 !important; } .flex-sm-grow-1 { flex-grow: 1 !important; } .flex-sm-shrink-0 { flex-shrink: 0 !important; } .flex-sm-shrink-1 { flex-shrink: 1 !important; } .flex-sm-wrap { flex-wrap: wrap !important; } .flex-sm-nowrap { flex-wrap: nowrap !important; } .flex-sm-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-sm-start { justify-content: flex-start !important; } .justify-content-sm-end { justify-content: flex-end !important; } .justify-content-sm-center { justify-content: center !important; } .justify-content-sm-between { justify-content: space-between !important; } .justify-content-sm-around { justify-content: space-around !important; } .justify-content-sm-evenly { justify-content: space-evenly !important; } .align-items-sm-start { align-items: flex-start !important; } .align-items-sm-end { align-items: flex-end !important; } .align-items-sm-center { align-items: center !important; } .align-items-sm-baseline { align-items: baseline !important; } .align-items-sm-stretch { align-items: stretch !important; } .align-content-sm-start { align-content: flex-start !important; } .align-content-sm-end { align-content: flex-end !important; } .align-content-sm-center { align-content: center !important; } .align-content-sm-between { align-content: space-between !important; } .align-content-sm-around { align-content: space-around !important; } .align-content-sm-stretch { align-content: stretch !important; } .align-self-sm-auto { align-self: auto !important; } .align-self-sm-start { align-self: flex-start !important; } .align-self-sm-end { align-self: flex-end !important; } .align-self-sm-center { align-self: center !important; } .align-self-sm-baseline { align-self: baseline !important; } .align-self-sm-stretch { align-self: stretch !important; } .order-sm-first { order: -1 !important; } .order-sm-0 { order: 0 !important; } .order-sm-1 { order: 1 !important; } .order-sm-2 { order: 2 !important; } .order-sm-3 { order: 3 !important; } .order-sm-4 { order: 4 !important; } .order-sm-5 { order: 5 !important; } .order-sm-last { order: 6 !important; } .m-sm-0 { margin: 0 !important; } .m-sm-1 { margin: 0.25rem !important; } .m-sm-2 { margin: 0.5rem !important; } .m-sm-3 { margin: 1rem !important; } .m-sm-4 { margin: 1.5rem !important; } .m-sm-5 { margin: 3rem !important; } .m-sm-auto { margin: auto !important; } .mx-sm-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-sm-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-sm-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-sm-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-sm-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-sm-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-sm-auto { margin-right: auto !important; margin-left: auto !important; } .my-sm-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-sm-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-sm-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-sm-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-sm-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-sm-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-sm-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-sm-0 { margin-top: 0 !important; } .mt-sm-1 { margin-top: 0.25rem !important; } .mt-sm-2 { margin-top: 0.5rem !important; } .mt-sm-3 { margin-top: 1rem !important; } .mt-sm-4 { margin-top: 1.5rem !important; } .mt-sm-5 { margin-top: 3rem !important; } .mt-sm-auto { margin-top: auto !important; } .me-sm-0 { margin-right: 0 !important; } .me-sm-1 { margin-right: 0.25rem !important; } .me-sm-2 { margin-right: 0.5rem !important; } .me-sm-3 { margin-right: 1rem !important; } .me-sm-4 { margin-right: 1.5rem !important; } .me-sm-5 { margin-right: 3rem !important; } .me-sm-auto { margin-right: auto !important; } .mb-sm-0 { margin-bottom: 0 !important; } .mb-sm-1 { margin-bottom: 0.25rem !important; } .mb-sm-2 { margin-bottom: 0.5rem !important; } .mb-sm-3 { margin-bottom: 1rem !important; } .mb-sm-4 { margin-bottom: 1.5rem !important; } .mb-sm-5 { margin-bottom: 3rem !important; } .mb-sm-auto { margin-bottom: auto !important; } .ms-sm-0 { margin-left: 0 !important; } .ms-sm-1 { margin-left: 0.25rem !important; } .ms-sm-2 { margin-left: 0.5rem !important; } .ms-sm-3 { margin-left: 1rem !important; } .ms-sm-4 { margin-left: 1.5rem !important; } .ms-sm-5 { margin-left: 3rem !important; } .ms-sm-auto { margin-left: auto !important; } .p-sm-0 { padding: 0 !important; } .p-sm-1 { padding: 0.25rem !important; } .p-sm-2 { padding: 0.5rem !important; } .p-sm-3 { padding: 1rem !important; } .p-sm-4 { padding: 1.5rem !important; } .p-sm-5 { padding: 3rem !important; } .px-sm-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-sm-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-sm-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-sm-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-sm-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-sm-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-sm-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-sm-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-sm-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-sm-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-sm-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-sm-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-sm-0 { padding-top: 0 !important; } .pt-sm-1 { padding-top: 0.25rem !important; } .pt-sm-2 { padding-top: 0.5rem !important; } .pt-sm-3 { padding-top: 1rem !important; } .pt-sm-4 { padding-top: 1.5rem !important; } .pt-sm-5 { padding-top: 3rem !important; } .pe-sm-0 { padding-right: 0 !important; } .pe-sm-1 { padding-right: 0.25rem !important; } .pe-sm-2 { padding-right: 0.5rem !important; } .pe-sm-3 { padding-right: 1rem !important; } .pe-sm-4 { padding-right: 1.5rem !important; } .pe-sm-5 { padding-right: 3rem !important; } .pb-sm-0 { padding-bottom: 0 !important; } .pb-sm-1 { padding-bottom: 0.25rem !important; } .pb-sm-2 { padding-bottom: 0.5rem !important; } .pb-sm-3 { padding-bottom: 1rem !important; } .pb-sm-4 { padding-bottom: 1.5rem !important; } .pb-sm-5 { padding-bottom: 3rem !important; } .ps-sm-0 { padding-left: 0 !important; } .ps-sm-1 { padding-left: 0.25rem !important; } .ps-sm-2 { padding-left: 0.5rem !important; } .ps-sm-3 { padding-left: 1rem !important; } .ps-sm-4 { padding-left: 1.5rem !important; } .ps-sm-5 { padding-left: 3rem !important; } } @media (min-width: 768px) { .d-md-inline { display: inline !important; } .d-md-inline-block { display: inline-block !important; } .d-md-block { display: block !important; } .d-md-grid { display: grid !important; } .d-md-table { display: table !important; } .d-md-table-row { display: table-row !important; } .d-md-table-cell { display: table-cell !important; } .d-md-flex { display: flex !important; } .d-md-inline-flex { display: inline-flex !important; } .d-md-none { display: none !important; } .flex-md-fill { flex: 1 1 auto !important; } .flex-md-row { flex-direction: row !important; } .flex-md-column { flex-direction: column !important; } .flex-md-row-reverse { flex-direction: row-reverse !important; } .flex-md-column-reverse { flex-direction: column-reverse !important; } .flex-md-grow-0 { flex-grow: 0 !important; } .flex-md-grow-1 { flex-grow: 1 !important; } .flex-md-shrink-0 { flex-shrink: 0 !important; } .flex-md-shrink-1 { flex-shrink: 1 !important; } .flex-md-wrap { flex-wrap: wrap !important; } .flex-md-nowrap { flex-wrap: nowrap !important; } .flex-md-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-md-start { justify-content: flex-start !important; } .justify-content-md-end { justify-content: flex-end !important; } .justify-content-md-center { justify-content: center !important; } .justify-content-md-between { justify-content: space-between !important; } .justify-content-md-around { justify-content: space-around !important; } .justify-content-md-evenly { justify-content: space-evenly !important; } .align-items-md-start { align-items: flex-start !important; } .align-items-md-end { align-items: flex-end !important; } .align-items-md-center { align-items: center !important; } .align-items-md-baseline { align-items: baseline !important; } .align-items-md-stretch { align-items: stretch !important; } .align-content-md-start { align-content: flex-start !important; } .align-content-md-end { align-content: flex-end !important; } .align-content-md-center { align-content: center !important; } .align-content-md-between { align-content: space-between !important; } .align-content-md-around { align-content: space-around !important; } .align-content-md-stretch { align-content: stretch !important; } .align-self-md-auto { align-self: auto !important; } .align-self-md-start { align-self: flex-start !important; } .align-self-md-end { align-self: flex-end !important; } .align-self-md-center { align-self: center !important; } .align-self-md-baseline { align-self: baseline !important; } .align-self-md-stretch { align-self: stretch !important; } .order-md-first { order: -1 !important; } .order-md-0 { order: 0 !important; } .order-md-1 { order: 1 !important; } .order-md-2 { order: 2 !important; } .order-md-3 { order: 3 !important; } .order-md-4 { order: 4 !important; } .order-md-5 { order: 5 !important; } .order-md-last { order: 6 !important; } .m-md-0 { margin: 0 !important; } .m-md-1 { margin: 0.25rem !important; } .m-md-2 { margin: 0.5rem !important; } .m-md-3 { margin: 1rem !important; } .m-md-4 { margin: 1.5rem !important; } .m-md-5 { margin: 3rem !important; } .m-md-auto { margin: auto !important; } .mx-md-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-md-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-md-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-md-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-md-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-md-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-md-auto { margin-right: auto !important; margin-left: auto !important; } .my-md-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-md-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-md-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-md-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-md-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-md-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-md-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-md-0 { margin-top: 0 !important; } .mt-md-1 { margin-top: 0.25rem !important; } .mt-md-2 { margin-top: 0.5rem !important; } .mt-md-3 { margin-top: 1rem !important; } .mt-md-4 { margin-top: 1.5rem !important; } .mt-md-5 { margin-top: 3rem !important; } .mt-md-auto { margin-top: auto !important; } .me-md-0 { margin-right: 0 !important; } .me-md-1 { margin-right: 0.25rem !important; } .me-md-2 { margin-right: 0.5rem !important; } .me-md-3 { margin-right: 1rem !important; } .me-md-4 { margin-right: 1.5rem !important; } .me-md-5 { margin-right: 3rem !important; } .me-md-auto { margin-right: auto !important; } .mb-md-0 { margin-bottom: 0 !important; } .mb-md-1 { margin-bottom: 0.25rem !important; } .mb-md-2 { margin-bottom: 0.5rem !important; } .mb-md-3 { margin-bottom: 1rem !important; } .mb-md-4 { margin-bottom: 1.5rem !important; } .mb-md-5 { margin-bottom: 3rem !important; } .mb-md-auto { margin-bottom: auto !important; } .ms-md-0 { margin-left: 0 !important; } .ms-md-1 { margin-left: 0.25rem !important; } .ms-md-2 { margin-left: 0.5rem !important; } .ms-md-3 { margin-left: 1rem !important; } .ms-md-4 { margin-left: 1.5rem !important; } .ms-md-5 { margin-left: 3rem !important; } .ms-md-auto { margin-left: auto !important; } .p-md-0 { padding: 0 !important; } .p-md-1 { padding: 0.25rem !important; } .p-md-2 { padding: 0.5rem !important; } .p-md-3 { padding: 1rem !important; } .p-md-4 { padding: 1.5rem !important; } .p-md-5 { padding: 3rem !important; } .px-md-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-md-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-md-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-md-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-md-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-md-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-md-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-md-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-md-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-md-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-md-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-md-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-md-0 { padding-top: 0 !important; } .pt-md-1 { padding-top: 0.25rem !important; } .pt-md-2 { padding-top: 0.5rem !important; } .pt-md-3 { padding-top: 1rem !important; } .pt-md-4 { padding-top: 1.5rem !important; } .pt-md-5 { padding-top: 3rem !important; } .pe-md-0 { padding-right: 0 !important; } .pe-md-1 { padding-right: 0.25rem !important; } .pe-md-2 { padding-right: 0.5rem !important; } .pe-md-3 { padding-right: 1rem !important; } .pe-md-4 { padding-right: 1.5rem !important; } .pe-md-5 { padding-right: 3rem !important; } .pb-md-0 { padding-bottom: 0 !important; } .pb-md-1 { padding-bottom: 0.25rem !important; } .pb-md-2 { padding-bottom: 0.5rem !important; } .pb-md-3 { padding-bottom: 1rem !important; } .pb-md-4 { padding-bottom: 1.5rem !important; } .pb-md-5 { padding-bottom: 3rem !important; } .ps-md-0 { padding-left: 0 !important; } .ps-md-1 { padding-left: 0.25rem !important; } .ps-md-2 { padding-left: 0.5rem !important; } .ps-md-3 { padding-left: 1rem !important; } .ps-md-4 { padding-left: 1.5rem !important; } .ps-md-5 { padding-left: 3rem !important; } } @media (min-width: 992px) { .d-lg-inline { display: inline !important; } .d-lg-inline-block { display: inline-block !important; } .d-lg-block { display: block !important; } .d-lg-grid { display: grid !important; } .d-lg-table { display: table !important; } .d-lg-table-row { display: table-row !important; } .d-lg-table-cell { display: table-cell !important; } .d-lg-flex { display: flex !important; } .d-lg-inline-flex { display: inline-flex !important; } .d-lg-none { display: none !important; } .flex-lg-fill { flex: 1 1 auto !important; } .flex-lg-row { flex-direction: row !important; } .flex-lg-column { flex-direction: column !important; } .flex-lg-row-reverse { flex-direction: row-reverse !important; } .flex-lg-column-reverse { flex-direction: column-reverse !important; } .flex-lg-grow-0 { flex-grow: 0 !important; } .flex-lg-grow-1 { flex-grow: 1 !important; } .flex-lg-shrink-0 { flex-shrink: 0 !important; } .flex-lg-shrink-1 { flex-shrink: 1 !important; } .flex-lg-wrap { flex-wrap: wrap !important; } .flex-lg-nowrap { flex-wrap: nowrap !important; } .flex-lg-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-lg-start { justify-content: flex-start !important; } .justify-content-lg-end { justify-content: flex-end !important; } .justify-content-lg-center { justify-content: center !important; } .justify-content-lg-between { justify-content: space-between !important; } .justify-content-lg-around { justify-content: space-around !important; } .justify-content-lg-evenly { justify-content: space-evenly !important; } .align-items-lg-start { align-items: flex-start !important; } .align-items-lg-end { align-items: flex-end !important; } .align-items-lg-center { align-items: center !important; } .align-items-lg-baseline { align-items: baseline !important; } .align-items-lg-stretch { align-items: stretch !important; } .align-content-lg-start { align-content: flex-start !important; } .align-content-lg-end { align-content: flex-end !important; } .align-content-lg-center { align-content: center !important; } .align-content-lg-between { align-content: space-between !important; } .align-content-lg-around { align-content: space-around !important; } .align-content-lg-stretch { align-content: stretch !important; } .align-self-lg-auto { align-self: auto !important; } .align-self-lg-start { align-self: flex-start !important; } .align-self-lg-end { align-self: flex-end !important; } .align-self-lg-center { align-self: center !important; } .align-self-lg-baseline { align-self: baseline !important; } .align-self-lg-stretch { align-self: stretch !important; } .order-lg-first { order: -1 !important; } .order-lg-0 { order: 0 !important; } .order-lg-1 { order: 1 !important; } .order-lg-2 { order: 2 !important; } .order-lg-3 { order: 3 !important; } .order-lg-4 { order: 4 !important; } .order-lg-5 { order: 5 !important; } .order-lg-last { order: 6 !important; } .m-lg-0 { margin: 0 !important; } .m-lg-1 { margin: 0.25rem !important; } .m-lg-2 { margin: 0.5rem !important; } .m-lg-3 { margin: 1rem !important; } .m-lg-4 { margin: 1.5rem !important; } .m-lg-5 { margin: 3rem !important; } .m-lg-auto { margin: auto !important; } .mx-lg-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-lg-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-lg-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-lg-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-lg-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-lg-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-lg-auto { margin-right: auto !important; margin-left: auto !important; } .my-lg-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-lg-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-lg-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-lg-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-lg-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-lg-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-lg-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-lg-0 { margin-top: 0 !important; } .mt-lg-1 { margin-top: 0.25rem !important; } .mt-lg-2 { margin-top: 0.5rem !important; } .mt-lg-3 { margin-top: 1rem !important; } .mt-lg-4 { margin-top: 1.5rem !important; } .mt-lg-5 { margin-top: 3rem !important; } .mt-lg-auto { margin-top: auto !important; } .me-lg-0 { margin-right: 0 !important; } .me-lg-1 { margin-right: 0.25rem !important; } .me-lg-2 { margin-right: 0.5rem !important; } .me-lg-3 { margin-right: 1rem !important; } .me-lg-4 { margin-right: 1.5rem !important; } .me-lg-5 { margin-right: 3rem !important; } .me-lg-auto { margin-right: auto !important; } .mb-lg-0 { margin-bottom: 0 !important; } .mb-lg-1 { margin-bottom: 0.25rem !important; } .mb-lg-2 { margin-bottom: 0.5rem !important; } .mb-lg-3 { margin-bottom: 1rem !important; } .mb-lg-4 { margin-bottom: 1.5rem !important; } .mb-lg-5 { margin-bottom: 3rem !important; } .mb-lg-auto { margin-bottom: auto !important; } .ms-lg-0 { margin-left: 0 !important; } .ms-lg-1 { margin-left: 0.25rem !important; } .ms-lg-2 { margin-left: 0.5rem !important; } .ms-lg-3 { margin-left: 1rem !important; } .ms-lg-4 { margin-left: 1.5rem !important; } .ms-lg-5 { margin-left: 3rem !important; } .ms-lg-auto { margin-left: auto !important; } .p-lg-0 { padding: 0 !important; } .p-lg-1 { padding: 0.25rem !important; } .p-lg-2 { padding: 0.5rem !important; } .p-lg-3 { padding: 1rem !important; } .p-lg-4 { padding: 1.5rem !important; } .p-lg-5 { padding: 3rem !important; } .px-lg-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-lg-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-lg-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-lg-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-lg-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-lg-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-lg-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-lg-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-lg-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-lg-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-lg-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-lg-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-lg-0 { padding-top: 0 !important; } .pt-lg-1 { padding-top: 0.25rem !important; } .pt-lg-2 { padding-top: 0.5rem !important; } .pt-lg-3 { padding-top: 1rem !important; } .pt-lg-4 { padding-top: 1.5rem !important; } .pt-lg-5 { padding-top: 3rem !important; } .pe-lg-0 { padding-right: 0 !important; } .pe-lg-1 { padding-right: 0.25rem !important; } .pe-lg-2 { padding-right: 0.5rem !important; } .pe-lg-3 { padding-right: 1rem !important; } .pe-lg-4 { padding-right: 1.5rem !important; } .pe-lg-5 { padding-right: 3rem !important; } .pb-lg-0 { padding-bottom: 0 !important; } .pb-lg-1 { padding-bottom: 0.25rem !important; } .pb-lg-2 { padding-bottom: 0.5rem !important; } .pb-lg-3 { padding-bottom: 1rem !important; } .pb-lg-4 { padding-bottom: 1.5rem !important; } .pb-lg-5 { padding-bottom: 3rem !important; } .ps-lg-0 { padding-left: 0 !important; } .ps-lg-1 { padding-left: 0.25rem !important; } .ps-lg-2 { padding-left: 0.5rem !important; } .ps-lg-3 { padding-left: 1rem !important; } .ps-lg-4 { padding-left: 1.5rem !important; } .ps-lg-5 { padding-left: 3rem !important; } } @media (min-width: 1200px) { .d-xl-inline { display: inline !important; } .d-xl-inline-block { display: inline-block !important; } .d-xl-block { display: block !important; } .d-xl-grid { display: grid !important; } .d-xl-table { display: table !important; } .d-xl-table-row { display: table-row !important; } .d-xl-table-cell { display: table-cell !important; } .d-xl-flex { display: flex !important; } .d-xl-inline-flex { display: inline-flex !important; } .d-xl-none { display: none !important; } .flex-xl-fill { flex: 1 1 auto !important; } .flex-xl-row { flex-direction: row !important; } .flex-xl-column { flex-direction: column !important; } .flex-xl-row-reverse { flex-direction: row-reverse !important; } .flex-xl-column-reverse { flex-direction: column-reverse !important; } .flex-xl-grow-0 { flex-grow: 0 !important; } .flex-xl-grow-1 { flex-grow: 1 !important; } .flex-xl-shrink-0 { flex-shrink: 0 !important; } .flex-xl-shrink-1 { flex-shrink: 1 !important; } .flex-xl-wrap { flex-wrap: wrap !important; } .flex-xl-nowrap { flex-wrap: nowrap !important; } .flex-xl-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-xl-start { justify-content: flex-start !important; } .justify-content-xl-end { justify-content: flex-end !important; } .justify-content-xl-center { justify-content: center !important; } .justify-content-xl-between { justify-content: space-between !important; } .justify-content-xl-around { justify-content: space-around !important; } .justify-content-xl-evenly { justify-content: space-evenly !important; } .align-items-xl-start { align-items: flex-start !important; } .align-items-xl-end { align-items: flex-end !important; } .align-items-xl-center { align-items: center !important; } .align-items-xl-baseline { align-items: baseline !important; } .align-items-xl-stretch { align-items: stretch !important; } .align-content-xl-start { align-content: flex-start !important; } .align-content-xl-end { align-content: flex-end !important; } .align-content-xl-center { align-content: center !important; } .align-content-xl-between { align-content: space-between !important; } .align-content-xl-around { align-content: space-around !important; } .align-content-xl-stretch { align-content: stretch !important; } .align-self-xl-auto { align-self: auto !important; } .align-self-xl-start { align-self: flex-start !important; } .align-self-xl-end { align-self: flex-end !important; } .align-self-xl-center { align-self: center !important; } .align-self-xl-baseline { align-self: baseline !important; } .align-self-xl-stretch { align-self: stretch !important; } .order-xl-first { order: -1 !important; } .order-xl-0 { order: 0 !important; } .order-xl-1 { order: 1 !important; } .order-xl-2 { order: 2 !important; } .order-xl-3 { order: 3 !important; } .order-xl-4 { order: 4 !important; } .order-xl-5 { order: 5 !important; } .order-xl-last { order: 6 !important; } .m-xl-0 { margin: 0 !important; } .m-xl-1 { margin: 0.25rem !important; } .m-xl-2 { margin: 0.5rem !important; } .m-xl-3 { margin: 1rem !important; } .m-xl-4 { margin: 1.5rem !important; } .m-xl-5 { margin: 3rem !important; } .m-xl-auto { margin: auto !important; } .mx-xl-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-xl-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-xl-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-xl-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-xl-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-xl-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-xl-auto { margin-right: auto !important; margin-left: auto !important; } .my-xl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xl-0 { margin-top: 0 !important; } .mt-xl-1 { margin-top: 0.25rem !important; } .mt-xl-2 { margin-top: 0.5rem !important; } .mt-xl-3 { margin-top: 1rem !important; } .mt-xl-4 { margin-top: 1.5rem !important; } .mt-xl-5 { margin-top: 3rem !important; } .mt-xl-auto { margin-top: auto !important; } .me-xl-0 { margin-right: 0 !important; } .me-xl-1 { margin-right: 0.25rem !important; } .me-xl-2 { margin-right: 0.5rem !important; } .me-xl-3 { margin-right: 1rem !important; } .me-xl-4 { margin-right: 1.5rem !important; } .me-xl-5 { margin-right: 3rem !important; } .me-xl-auto { margin-right: auto !important; } .mb-xl-0 { margin-bottom: 0 !important; } .mb-xl-1 { margin-bottom: 0.25rem !important; } .mb-xl-2 { margin-bottom: 0.5rem !important; } .mb-xl-3 { margin-bottom: 1rem !important; } .mb-xl-4 { margin-bottom: 1.5rem !important; } .mb-xl-5 { margin-bottom: 3rem !important; } .mb-xl-auto { margin-bottom: auto !important; } .ms-xl-0 { margin-left: 0 !important; } .ms-xl-1 { margin-left: 0.25rem !important; } .ms-xl-2 { margin-left: 0.5rem !important; } .ms-xl-3 { margin-left: 1rem !important; } .ms-xl-4 { margin-left: 1.5rem !important; } .ms-xl-5 { margin-left: 3rem !important; } .ms-xl-auto { margin-left: auto !important; } .p-xl-0 { padding: 0 !important; } .p-xl-1 { padding: 0.25rem !important; } .p-xl-2 { padding: 0.5rem !important; } .p-xl-3 { padding: 1rem !important; } .p-xl-4 { padding: 1.5rem !important; } .p-xl-5 { padding: 3rem !important; } .px-xl-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-xl-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-xl-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-xl-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-xl-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-xl-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-xl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xl-0 { padding-top: 0 !important; } .pt-xl-1 { padding-top: 0.25rem !important; } .pt-xl-2 { padding-top: 0.5rem !important; } .pt-xl-3 { padding-top: 1rem !important; } .pt-xl-4 { padding-top: 1.5rem !important; } .pt-xl-5 { padding-top: 3rem !important; } .pe-xl-0 { padding-right: 0 !important; } .pe-xl-1 { padding-right: 0.25rem !important; } .pe-xl-2 { padding-right: 0.5rem !important; } .pe-xl-3 { padding-right: 1rem !important; } .pe-xl-4 { padding-right: 1.5rem !important; } .pe-xl-5 { padding-right: 3rem !important; } .pb-xl-0 { padding-bottom: 0 !important; } .pb-xl-1 { padding-bottom: 0.25rem !important; } .pb-xl-2 { padding-bottom: 0.5rem !important; } .pb-xl-3 { padding-bottom: 1rem !important; } .pb-xl-4 { padding-bottom: 1.5rem !important; } .pb-xl-5 { padding-bottom: 3rem !important; } .ps-xl-0 { padding-left: 0 !important; } .ps-xl-1 { padding-left: 0.25rem !important; } .ps-xl-2 { padding-left: 0.5rem !important; } .ps-xl-3 { padding-left: 1rem !important; } .ps-xl-4 { padding-left: 1.5rem !important; } .ps-xl-5 { padding-left: 3rem !important; } } @media (min-width: 1400px) { .d-xxl-inline { display: inline !important; } .d-xxl-inline-block { display: inline-block !important; } .d-xxl-block { display: block !important; } .d-xxl-grid { display: grid !important; } .d-xxl-table { display: table !important; } .d-xxl-table-row { display: table-row !important; } .d-xxl-table-cell { display: table-cell !important; } .d-xxl-flex { display: flex !important; } .d-xxl-inline-flex { display: inline-flex !important; } .d-xxl-none { display: none !important; } .flex-xxl-fill { flex: 1 1 auto !important; } .flex-xxl-row { flex-direction: row !important; } .flex-xxl-column { flex-direction: column !important; } .flex-xxl-row-reverse { flex-direction: row-reverse !important; } .flex-xxl-column-reverse { flex-direction: column-reverse !important; } .flex-xxl-grow-0 { flex-grow: 0 !important; } .flex-xxl-grow-1 { flex-grow: 1 !important; } .flex-xxl-shrink-0 { flex-shrink: 0 !important; } .flex-xxl-shrink-1 { flex-shrink: 1 !important; } .flex-xxl-wrap { flex-wrap: wrap !important; } .flex-xxl-nowrap { flex-wrap: nowrap !important; } .flex-xxl-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-xxl-start { justify-content: flex-start !important; } .justify-content-xxl-end { justify-content: flex-end !important; } .justify-content-xxl-center { justify-content: center !important; } .justify-content-xxl-between { justify-content: space-between !important; } .justify-content-xxl-around { justify-content: space-around !important; } .justify-content-xxl-evenly { justify-content: space-evenly !important; } .align-items-xxl-start { align-items: flex-start !important; } .align-items-xxl-end { align-items: flex-end !important; } .align-items-xxl-center { align-items: center !important; } .align-items-xxl-baseline { align-items: baseline !important; } .align-items-xxl-stretch { align-items: stretch !important; } .align-content-xxl-start { align-content: flex-start !important; } .align-content-xxl-end { align-content: flex-end !important; } .align-content-xxl-center { align-content: center !important; } .align-content-xxl-between { align-content: space-between !important; } .align-content-xxl-around { align-content: space-around !important; } .align-content-xxl-stretch { align-content: stretch !important; } .align-self-xxl-auto { align-self: auto !important; } .align-self-xxl-start { align-self: flex-start !important; } .align-self-xxl-end { align-self: flex-end !important; } .align-self-xxl-center { align-self: center !important; } .align-self-xxl-baseline { align-self: baseline !important; } .align-self-xxl-stretch { align-self: stretch !important; } .order-xxl-first { order: -1 !important; } .order-xxl-0 { order: 0 !important; } .order-xxl-1 { order: 1 !important; } .order-xxl-2 { order: 2 !important; } .order-xxl-3 { order: 3 !important; } .order-xxl-4 { order: 4 !important; } .order-xxl-5 { order: 5 !important; } .order-xxl-last { order: 6 !important; } .m-xxl-0 { margin: 0 !important; } .m-xxl-1 { margin: 0.25rem !important; } .m-xxl-2 { margin: 0.5rem !important; } .m-xxl-3 { margin: 1rem !important; } .m-xxl-4 { margin: 1.5rem !important; } .m-xxl-5 { margin: 3rem !important; } .m-xxl-auto { margin: auto !important; } .mx-xxl-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-xxl-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-xxl-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-xxl-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-xxl-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-xxl-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-xxl-auto { margin-right: auto !important; margin-left: auto !important; } .my-xxl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xxl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xxl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xxl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xxl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xxl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xxl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xxl-0 { margin-top: 0 !important; } .mt-xxl-1 { margin-top: 0.25rem !important; } .mt-xxl-2 { margin-top: 0.5rem !important; } .mt-xxl-3 { margin-top: 1rem !important; } .mt-xxl-4 { margin-top: 1.5rem !important; } .mt-xxl-5 { margin-top: 3rem !important; } .mt-xxl-auto { margin-top: auto !important; } .me-xxl-0 { margin-right: 0 !important; } .me-xxl-1 { margin-right: 0.25rem !important; } .me-xxl-2 { margin-right: 0.5rem !important; } .me-xxl-3 { margin-right: 1rem !important; } .me-xxl-4 { margin-right: 1.5rem !important; } .me-xxl-5 { margin-right: 3rem !important; } .me-xxl-auto { margin-right: auto !important; } .mb-xxl-0 { margin-bottom: 0 !important; } .mb-xxl-1 { margin-bottom: 0.25rem !important; } .mb-xxl-2 { margin-bottom: 0.5rem !important; } .mb-xxl-3 { margin-bottom: 1rem !important; } .mb-xxl-4 { margin-bottom: 1.5rem !important; } .mb-xxl-5 { margin-bottom: 3rem !important; } .mb-xxl-auto { margin-bottom: auto !important; } .ms-xxl-0 { margin-left: 0 !important; } .ms-xxl-1 { margin-left: 0.25rem !important; } .ms-xxl-2 { margin-left: 0.5rem !important; } .ms-xxl-3 { margin-left: 1rem !important; } .ms-xxl-4 { margin-left: 1.5rem !important; } .ms-xxl-5 { margin-left: 3rem !important; } .ms-xxl-auto { margin-left: auto !important; } .p-xxl-0 { padding: 0 !important; } .p-xxl-1 { padding: 0.25rem !important; } .p-xxl-2 { padding: 0.5rem !important; } .p-xxl-3 { padding: 1rem !important; } .p-xxl-4 { padding: 1.5rem !important; } .p-xxl-5 { padding: 3rem !important; } .px-xxl-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-xxl-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-xxl-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-xxl-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-xxl-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-xxl-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-xxl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xxl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xxl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xxl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xxl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xxl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xxl-0 { padding-top: 0 !important; } .pt-xxl-1 { padding-top: 0.25rem !important; } .pt-xxl-2 { padding-top: 0.5rem !important; } .pt-xxl-3 { padding-top: 1rem !important; } .pt-xxl-4 { padding-top: 1.5rem !important; } .pt-xxl-5 { padding-top: 3rem !important; } .pe-xxl-0 { padding-right: 0 !important; } .pe-xxl-1 { padding-right: 0.25rem !important; } .pe-xxl-2 { padding-right: 0.5rem !important; } .pe-xxl-3 { padding-right: 1rem !important; } .pe-xxl-4 { padding-right: 1.5rem !important; } .pe-xxl-5 { padding-right: 3rem !important; } .pb-xxl-0 { padding-bottom: 0 !important; } .pb-xxl-1 { padding-bottom: 0.25rem !important; } .pb-xxl-2 { padding-bottom: 0.5rem !important; } .pb-xxl-3 { padding-bottom: 1rem !important; } .pb-xxl-4 { padding-bottom: 1.5rem !important; } .pb-xxl-5 { padding-bottom: 3rem !important; } .ps-xxl-0 { padding-left: 0 !important; } .ps-xxl-1 { padding-left: 0.25rem !important; } .ps-xxl-2 { padding-left: 0.5rem !important; } .ps-xxl-3 { padding-left: 1rem !important; } .ps-xxl-4 { padding-left: 1.5rem !important; } .ps-xxl-5 { padding-left: 3rem !important; } } @media print { .d-print-inline { display: inline !important; } .d-print-inline-block { display: inline-block !important; } .d-print-block { display: block !important; } .d-print-grid { display: grid !important; } .d-print-table { display: table !important; } .d-print-table-row { display: table-row !important; } .d-print-table-cell { display: table-cell !important; } .d-print-flex { display: flex !important; } .d-print-inline-flex { display: inline-flex !important; } .d-print-none { display: none !important; } } /*# sourceMappingURL=bootstrap-grid.css.map */ ================================================ FILE: examples/tailwind-and-bootstrap-example/css-modules/bootstrap/bootstrap-grid.rtl.css ================================================ /*! * Bootstrap Grid v5.1.3 (https://getbootstrap.com/) * Copyright 2011-2021 The Bootstrap Authors * Copyright 2011-2021 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ :root { --bs-blue: #0d6efd; --bs-indigo: #6610f2; --bs-purple: #6f42c1; --bs-pink: #d63384; --bs-red: #dc3545; --bs-orange: #fd7e14; --bs-yellow: #ffc107; --bs-green: #198754; --bs-teal: #20c997; --bs-cyan: #0dcaf0; --bs-white: #fff; --bs-gray: #6c757d; --bs-gray-dark: #343a40; --bs-gray-100: #f8f9fa; --bs-gray-200: #e9ecef; --bs-gray-300: #dee2e6; --bs-gray-400: #ced4da; --bs-gray-500: #adb5bd; --bs-gray-600: #6c757d; --bs-gray-700: #495057; --bs-gray-800: #343a40; --bs-gray-900: #212529; --bs-primary: #0d6efd; --bs-secondary: #6c757d; --bs-success: #198754; --bs-info: #0dcaf0; --bs-warning: #ffc107; --bs-danger: #dc3545; --bs-light: #f8f9fa; --bs-dark: #212529; --bs-primary-rgb: 13, 110, 253; --bs-secondary-rgb: 108, 117, 125; --bs-success-rgb: 25, 135, 84; --bs-info-rgb: 13, 202, 240; --bs-warning-rgb: 255, 193, 7; --bs-danger-rgb: 220, 53, 69; --bs-light-rgb: 248, 249, 250; --bs-dark-rgb: 33, 37, 41; --bs-white-rgb: 255, 255, 255; --bs-black-rgb: 0, 0, 0; --bs-body-color-rgb: 33, 37, 41; --bs-body-bg-rgb: 255, 255, 255; --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --bs-gradient: linear-gradient( 180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0) ); --bs-body-font-family: var(--bs-font-sans-serif); --bs-body-font-size: 1rem; --bs-body-font-weight: 400; --bs-body-line-height: 1.5; --bs-body-color: #212529; --bs-body-bg: #fff; } .container, .container-fluid, .container-xxl, .container-xl, .container-lg, .container-md, .container-sm { width: 100%; padding-left: var(--bs-gutter-x, 0.75rem); padding-right: var(--bs-gutter-x, 0.75rem); margin-left: auto; margin-right: auto; } @media (min-width: 576px) { .container-sm, .container { max-width: 540px; } } @media (min-width: 768px) { .container-md, .container-sm, .container { max-width: 720px; } } @media (min-width: 992px) { .container-lg, .container-md, .container-sm, .container { max-width: 960px; } } @media (min-width: 1200px) { .container-xl, .container-lg, .container-md, .container-sm, .container { max-width: 1140px; } } @media (min-width: 1400px) { .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { max-width: 1320px; } } .row { --bs-gutter-x: 1.5rem; --bs-gutter-y: 0; display: flex; flex-wrap: wrap; margin-top: calc(-1 * var(--bs-gutter-y)); margin-left: calc(-0.5 * var(--bs-gutter-x)); margin-right: calc(-0.5 * var(--bs-gutter-x)); } .row > * { box-sizing: border-box; flex-shrink: 0; width: 100%; max-width: 100%; padding-left: calc(var(--bs-gutter-x) * 0.5); padding-right: calc(var(--bs-gutter-x) * 0.5); margin-top: var(--bs-gutter-y); } .col { flex: 1 0 0%; } .row-cols-auto > * { flex: 0 0 auto; width: auto; } .row-cols-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-auto { flex: 0 0 auto; width: auto; } .col-1 { flex: 0 0 auto; width: 8.33333333%; } .col-2 { flex: 0 0 auto; width: 16.66666667%; } .col-3 { flex: 0 0 auto; width: 25%; } .col-4 { flex: 0 0 auto; width: 33.33333333%; } .col-5 { flex: 0 0 auto; width: 41.66666667%; } .col-6 { flex: 0 0 auto; width: 50%; } .col-7 { flex: 0 0 auto; width: 58.33333333%; } .col-8 { flex: 0 0 auto; width: 66.66666667%; } .col-9 { flex: 0 0 auto; width: 75%; } .col-10 { flex: 0 0 auto; width: 83.33333333%; } .col-11 { flex: 0 0 auto; width: 91.66666667%; } .col-12 { flex: 0 0 auto; width: 100%; } .offset-1 { margin-right: 8.33333333%; } .offset-2 { margin-right: 16.66666667%; } .offset-3 { margin-right: 25%; } .offset-4 { margin-right: 33.33333333%; } .offset-5 { margin-right: 41.66666667%; } .offset-6 { margin-right: 50%; } .offset-7 { margin-right: 58.33333333%; } .offset-8 { margin-right: 66.66666667%; } .offset-9 { margin-right: 75%; } .offset-10 { margin-right: 83.33333333%; } .offset-11 { margin-right: 91.66666667%; } .g-0, .gx-0 { --bs-gutter-x: 0; } .g-0, .gy-0 { --bs-gutter-y: 0; } .g-1, .gx-1 { --bs-gutter-x: 0.25rem; } .g-1, .gy-1 { --bs-gutter-y: 0.25rem; } .g-2, .gx-2 { --bs-gutter-x: 0.5rem; } .g-2, .gy-2 { --bs-gutter-y: 0.5rem; } .g-3, .gx-3 { --bs-gutter-x: 1rem; } .g-3, .gy-3 { --bs-gutter-y: 1rem; } .g-4, .gx-4 { --bs-gutter-x: 1.5rem; } .g-4, .gy-4 { --bs-gutter-y: 1.5rem; } .g-5, .gx-5 { --bs-gutter-x: 3rem; } .g-5, .gy-5 { --bs-gutter-y: 3rem; } @media (min-width: 576px) { .col-sm { flex: 1 0 0%; } .row-cols-sm-auto > * { flex: 0 0 auto; width: auto; } .row-cols-sm-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-sm-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-sm-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-sm-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-sm-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-sm-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-sm-auto { flex: 0 0 auto; width: auto; } .col-sm-1 { flex: 0 0 auto; width: 8.33333333%; } .col-sm-2 { flex: 0 0 auto; width: 16.66666667%; } .col-sm-3 { flex: 0 0 auto; width: 25%; } .col-sm-4 { flex: 0 0 auto; width: 33.33333333%; } .col-sm-5 { flex: 0 0 auto; width: 41.66666667%; } .col-sm-6 { flex: 0 0 auto; width: 50%; } .col-sm-7 { flex: 0 0 auto; width: 58.33333333%; } .col-sm-8 { flex: 0 0 auto; width: 66.66666667%; } .col-sm-9 { flex: 0 0 auto; width: 75%; } .col-sm-10 { flex: 0 0 auto; width: 83.33333333%; } .col-sm-11 { flex: 0 0 auto; width: 91.66666667%; } .col-sm-12 { flex: 0 0 auto; width: 100%; } .offset-sm-0 { margin-right: 0; } .offset-sm-1 { margin-right: 8.33333333%; } .offset-sm-2 { margin-right: 16.66666667%; } .offset-sm-3 { margin-right: 25%; } .offset-sm-4 { margin-right: 33.33333333%; } .offset-sm-5 { margin-right: 41.66666667%; } .offset-sm-6 { margin-right: 50%; } .offset-sm-7 { margin-right: 58.33333333%; } .offset-sm-8 { margin-right: 66.66666667%; } .offset-sm-9 { margin-right: 75%; } .offset-sm-10 { margin-right: 83.33333333%; } .offset-sm-11 { margin-right: 91.66666667%; } .g-sm-0, .gx-sm-0 { --bs-gutter-x: 0; } .g-sm-0, .gy-sm-0 { --bs-gutter-y: 0; } .g-sm-1, .gx-sm-1 { --bs-gutter-x: 0.25rem; } .g-sm-1, .gy-sm-1 { --bs-gutter-y: 0.25rem; } .g-sm-2, .gx-sm-2 { --bs-gutter-x: 0.5rem; } .g-sm-2, .gy-sm-2 { --bs-gutter-y: 0.5rem; } .g-sm-3, .gx-sm-3 { --bs-gutter-x: 1rem; } .g-sm-3, .gy-sm-3 { --bs-gutter-y: 1rem; } .g-sm-4, .gx-sm-4 { --bs-gutter-x: 1.5rem; } .g-sm-4, .gy-sm-4 { --bs-gutter-y: 1.5rem; } .g-sm-5, .gx-sm-5 { --bs-gutter-x: 3rem; } .g-sm-5, .gy-sm-5 { --bs-gutter-y: 3rem; } } @media (min-width: 768px) { .col-md { flex: 1 0 0%; } .row-cols-md-auto > * { flex: 0 0 auto; width: auto; } .row-cols-md-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-md-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-md-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-md-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-md-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-md-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-md-auto { flex: 0 0 auto; width: auto; } .col-md-1 { flex: 0 0 auto; width: 8.33333333%; } .col-md-2 { flex: 0 0 auto; width: 16.66666667%; } .col-md-3 { flex: 0 0 auto; width: 25%; } .col-md-4 { flex: 0 0 auto; width: 33.33333333%; } .col-md-5 { flex: 0 0 auto; width: 41.66666667%; } .col-md-6 { flex: 0 0 auto; width: 50%; } .col-md-7 { flex: 0 0 auto; width: 58.33333333%; } .col-md-8 { flex: 0 0 auto; width: 66.66666667%; } .col-md-9 { flex: 0 0 auto; width: 75%; } .col-md-10 { flex: 0 0 auto; width: 83.33333333%; } .col-md-11 { flex: 0 0 auto; width: 91.66666667%; } .col-md-12 { flex: 0 0 auto; width: 100%; } .offset-md-0 { margin-right: 0; } .offset-md-1 { margin-right: 8.33333333%; } .offset-md-2 { margin-right: 16.66666667%; } .offset-md-3 { margin-right: 25%; } .offset-md-4 { margin-right: 33.33333333%; } .offset-md-5 { margin-right: 41.66666667%; } .offset-md-6 { margin-right: 50%; } .offset-md-7 { margin-right: 58.33333333%; } .offset-md-8 { margin-right: 66.66666667%; } .offset-md-9 { margin-right: 75%; } .offset-md-10 { margin-right: 83.33333333%; } .offset-md-11 { margin-right: 91.66666667%; } .g-md-0, .gx-md-0 { --bs-gutter-x: 0; } .g-md-0, .gy-md-0 { --bs-gutter-y: 0; } .g-md-1, .gx-md-1 { --bs-gutter-x: 0.25rem; } .g-md-1, .gy-md-1 { --bs-gutter-y: 0.25rem; } .g-md-2, .gx-md-2 { --bs-gutter-x: 0.5rem; } .g-md-2, .gy-md-2 { --bs-gutter-y: 0.5rem; } .g-md-3, .gx-md-3 { --bs-gutter-x: 1rem; } .g-md-3, .gy-md-3 { --bs-gutter-y: 1rem; } .g-md-4, .gx-md-4 { --bs-gutter-x: 1.5rem; } .g-md-4, .gy-md-4 { --bs-gutter-y: 1.5rem; } .g-md-5, .gx-md-5 { --bs-gutter-x: 3rem; } .g-md-5, .gy-md-5 { --bs-gutter-y: 3rem; } } @media (min-width: 992px) { .col-lg { flex: 1 0 0%; } .row-cols-lg-auto > * { flex: 0 0 auto; width: auto; } .row-cols-lg-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-lg-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-lg-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-lg-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-lg-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-lg-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-lg-auto { flex: 0 0 auto; width: auto; } .col-lg-1 { flex: 0 0 auto; width: 8.33333333%; } .col-lg-2 { flex: 0 0 auto; width: 16.66666667%; } .col-lg-3 { flex: 0 0 auto; width: 25%; } .col-lg-4 { flex: 0 0 auto; width: 33.33333333%; } .col-lg-5 { flex: 0 0 auto; width: 41.66666667%; } .col-lg-6 { flex: 0 0 auto; width: 50%; } .col-lg-7 { flex: 0 0 auto; width: 58.33333333%; } .col-lg-8 { flex: 0 0 auto; width: 66.66666667%; } .col-lg-9 { flex: 0 0 auto; width: 75%; } .col-lg-10 { flex: 0 0 auto; width: 83.33333333%; } .col-lg-11 { flex: 0 0 auto; width: 91.66666667%; } .col-lg-12 { flex: 0 0 auto; width: 100%; } .offset-lg-0 { margin-right: 0; } .offset-lg-1 { margin-right: 8.33333333%; } .offset-lg-2 { margin-right: 16.66666667%; } .offset-lg-3 { margin-right: 25%; } .offset-lg-4 { margin-right: 33.33333333%; } .offset-lg-5 { margin-right: 41.66666667%; } .offset-lg-6 { margin-right: 50%; } .offset-lg-7 { margin-right: 58.33333333%; } .offset-lg-8 { margin-right: 66.66666667%; } .offset-lg-9 { margin-right: 75%; } .offset-lg-10 { margin-right: 83.33333333%; } .offset-lg-11 { margin-right: 91.66666667%; } .g-lg-0, .gx-lg-0 { --bs-gutter-x: 0; } .g-lg-0, .gy-lg-0 { --bs-gutter-y: 0; } .g-lg-1, .gx-lg-1 { --bs-gutter-x: 0.25rem; } .g-lg-1, .gy-lg-1 { --bs-gutter-y: 0.25rem; } .g-lg-2, .gx-lg-2 { --bs-gutter-x: 0.5rem; } .g-lg-2, .gy-lg-2 { --bs-gutter-y: 0.5rem; } .g-lg-3, .gx-lg-3 { --bs-gutter-x: 1rem; } .g-lg-3, .gy-lg-3 { --bs-gutter-y: 1rem; } .g-lg-4, .gx-lg-4 { --bs-gutter-x: 1.5rem; } .g-lg-4, .gy-lg-4 { --bs-gutter-y: 1.5rem; } .g-lg-5, .gx-lg-5 { --bs-gutter-x: 3rem; } .g-lg-5, .gy-lg-5 { --bs-gutter-y: 3rem; } } @media (min-width: 1200px) { .col-xl { flex: 1 0 0%; } .row-cols-xl-auto > * { flex: 0 0 auto; width: auto; } .row-cols-xl-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-xl-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-xl-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-xl-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-xl-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-xl-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-xl-auto { flex: 0 0 auto; width: auto; } .col-xl-1 { flex: 0 0 auto; width: 8.33333333%; } .col-xl-2 { flex: 0 0 auto; width: 16.66666667%; } .col-xl-3 { flex: 0 0 auto; width: 25%; } .col-xl-4 { flex: 0 0 auto; width: 33.33333333%; } .col-xl-5 { flex: 0 0 auto; width: 41.66666667%; } .col-xl-6 { flex: 0 0 auto; width: 50%; } .col-xl-7 { flex: 0 0 auto; width: 58.33333333%; } .col-xl-8 { flex: 0 0 auto; width: 66.66666667%; } .col-xl-9 { flex: 0 0 auto; width: 75%; } .col-xl-10 { flex: 0 0 auto; width: 83.33333333%; } .col-xl-11 { flex: 0 0 auto; width: 91.66666667%; } .col-xl-12 { flex: 0 0 auto; width: 100%; } .offset-xl-0 { margin-right: 0; } .offset-xl-1 { margin-right: 8.33333333%; } .offset-xl-2 { margin-right: 16.66666667%; } .offset-xl-3 { margin-right: 25%; } .offset-xl-4 { margin-right: 33.33333333%; } .offset-xl-5 { margin-right: 41.66666667%; } .offset-xl-6 { margin-right: 50%; } .offset-xl-7 { margin-right: 58.33333333%; } .offset-xl-8 { margin-right: 66.66666667%; } .offset-xl-9 { margin-right: 75%; } .offset-xl-10 { margin-right: 83.33333333%; } .offset-xl-11 { margin-right: 91.66666667%; } .g-xl-0, .gx-xl-0 { --bs-gutter-x: 0; } .g-xl-0, .gy-xl-0 { --bs-gutter-y: 0; } .g-xl-1, .gx-xl-1 { --bs-gutter-x: 0.25rem; } .g-xl-1, .gy-xl-1 { --bs-gutter-y: 0.25rem; } .g-xl-2, .gx-xl-2 { --bs-gutter-x: 0.5rem; } .g-xl-2, .gy-xl-2 { --bs-gutter-y: 0.5rem; } .g-xl-3, .gx-xl-3 { --bs-gutter-x: 1rem; } .g-xl-3, .gy-xl-3 { --bs-gutter-y: 1rem; } .g-xl-4, .gx-xl-4 { --bs-gutter-x: 1.5rem; } .g-xl-4, .gy-xl-4 { --bs-gutter-y: 1.5rem; } .g-xl-5, .gx-xl-5 { --bs-gutter-x: 3rem; } .g-xl-5, .gy-xl-5 { --bs-gutter-y: 3rem; } } @media (min-width: 1400px) { .col-xxl { flex: 1 0 0%; } .row-cols-xxl-auto > * { flex: 0 0 auto; width: auto; } .row-cols-xxl-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-xxl-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-xxl-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-xxl-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-xxl-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-xxl-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-xxl-auto { flex: 0 0 auto; width: auto; } .col-xxl-1 { flex: 0 0 auto; width: 8.33333333%; } .col-xxl-2 { flex: 0 0 auto; width: 16.66666667%; } .col-xxl-3 { flex: 0 0 auto; width: 25%; } .col-xxl-4 { flex: 0 0 auto; width: 33.33333333%; } .col-xxl-5 { flex: 0 0 auto; width: 41.66666667%; } .col-xxl-6 { flex: 0 0 auto; width: 50%; } .col-xxl-7 { flex: 0 0 auto; width: 58.33333333%; } .col-xxl-8 { flex: 0 0 auto; width: 66.66666667%; } .col-xxl-9 { flex: 0 0 auto; width: 75%; } .col-xxl-10 { flex: 0 0 auto; width: 83.33333333%; } .col-xxl-11 { flex: 0 0 auto; width: 91.66666667%; } .col-xxl-12 { flex: 0 0 auto; width: 100%; } .offset-xxl-0 { margin-right: 0; } .offset-xxl-1 { margin-right: 8.33333333%; } .offset-xxl-2 { margin-right: 16.66666667%; } .offset-xxl-3 { margin-right: 25%; } .offset-xxl-4 { margin-right: 33.33333333%; } .offset-xxl-5 { margin-right: 41.66666667%; } .offset-xxl-6 { margin-right: 50%; } .offset-xxl-7 { margin-right: 58.33333333%; } .offset-xxl-8 { margin-right: 66.66666667%; } .offset-xxl-9 { margin-right: 75%; } .offset-xxl-10 { margin-right: 83.33333333%; } .offset-xxl-11 { margin-right: 91.66666667%; } .g-xxl-0, .gx-xxl-0 { --bs-gutter-x: 0; } .g-xxl-0, .gy-xxl-0 { --bs-gutter-y: 0; } .g-xxl-1, .gx-xxl-1 { --bs-gutter-x: 0.25rem; } .g-xxl-1, .gy-xxl-1 { --bs-gutter-y: 0.25rem; } .g-xxl-2, .gx-xxl-2 { --bs-gutter-x: 0.5rem; } .g-xxl-2, .gy-xxl-2 { --bs-gutter-y: 0.5rem; } .g-xxl-3, .gx-xxl-3 { --bs-gutter-x: 1rem; } .g-xxl-3, .gy-xxl-3 { --bs-gutter-y: 1rem; } .g-xxl-4, .gx-xxl-4 { --bs-gutter-x: 1.5rem; } .g-xxl-4, .gy-xxl-4 { --bs-gutter-y: 1.5rem; } .g-xxl-5, .gx-xxl-5 { --bs-gutter-x: 3rem; } .g-xxl-5, .gy-xxl-5 { --bs-gutter-y: 3rem; } } .d-inline { display: inline !important; } .d-inline-block { display: inline-block !important; } .d-block { display: block !important; } .d-grid { display: grid !important; } .d-table { display: table !important; } .d-table-row { display: table-row !important; } .d-table-cell { display: table-cell !important; } .d-flex { display: flex !important; } .d-inline-flex { display: inline-flex !important; } .d-none { display: none !important; } .flex-fill { flex: 1 1 auto !important; } .flex-row { flex-direction: row !important; } .flex-column { flex-direction: column !important; } .flex-row-reverse { flex-direction: row-reverse !important; } .flex-column-reverse { flex-direction: column-reverse !important; } .flex-grow-0 { flex-grow: 0 !important; } .flex-grow-1 { flex-grow: 1 !important; } .flex-shrink-0 { flex-shrink: 0 !important; } .flex-shrink-1 { flex-shrink: 1 !important; } .flex-wrap { flex-wrap: wrap !important; } .flex-nowrap { flex-wrap: nowrap !important; } .flex-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-start { justify-content: flex-start !important; } .justify-content-end { justify-content: flex-end !important; } .justify-content-center { justify-content: center !important; } .justify-content-between { justify-content: space-between !important; } .justify-content-around { justify-content: space-around !important; } .justify-content-evenly { justify-content: space-evenly !important; } .align-items-start { align-items: flex-start !important; } .align-items-end { align-items: flex-end !important; } .align-items-center { align-items: center !important; } .align-items-baseline { align-items: baseline !important; } .align-items-stretch { align-items: stretch !important; } .align-content-start { align-content: flex-start !important; } .align-content-end { align-content: flex-end !important; } .align-content-center { align-content: center !important; } .align-content-between { align-content: space-between !important; } .align-content-around { align-content: space-around !important; } .align-content-stretch { align-content: stretch !important; } .align-self-auto { align-self: auto !important; } .align-self-start { align-self: flex-start !important; } .align-self-end { align-self: flex-end !important; } .align-self-center { align-self: center !important; } .align-self-baseline { align-self: baseline !important; } .align-self-stretch { align-self: stretch !important; } .order-first { order: -1 !important; } .order-0 { order: 0 !important; } .order-1 { order: 1 !important; } .order-2 { order: 2 !important; } .order-3 { order: 3 !important; } .order-4 { order: 4 !important; } .order-5 { order: 5 !important; } .order-last { order: 6 !important; } .m-0 { margin: 0 !important; } .m-1 { margin: 0.25rem !important; } .m-2 { margin: 0.5rem !important; } .m-3 { margin: 1rem !important; } .m-4 { margin: 1.5rem !important; } .m-5 { margin: 3rem !important; } .m-auto { margin: auto !important; } .mx-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-auto { margin-left: auto !important; margin-right: auto !important; } .my-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-0 { margin-top: 0 !important; } .mt-1 { margin-top: 0.25rem !important; } .mt-2 { margin-top: 0.5rem !important; } .mt-3 { margin-top: 1rem !important; } .mt-4 { margin-top: 1.5rem !important; } .mt-5 { margin-top: 3rem !important; } .mt-auto { margin-top: auto !important; } .me-0 { margin-left: 0 !important; } .me-1 { margin-left: 0.25rem !important; } .me-2 { margin-left: 0.5rem !important; } .me-3 { margin-left: 1rem !important; } .me-4 { margin-left: 1.5rem !important; } .me-5 { margin-left: 3rem !important; } .me-auto { margin-left: auto !important; } .mb-0 { margin-bottom: 0 !important; } .mb-1 { margin-bottom: 0.25rem !important; } .mb-2 { margin-bottom: 0.5rem !important; } .mb-3 { margin-bottom: 1rem !important; } .mb-4 { margin-bottom: 1.5rem !important; } .mb-5 { margin-bottom: 3rem !important; } .mb-auto { margin-bottom: auto !important; } .ms-0 { margin-right: 0 !important; } .ms-1 { margin-right: 0.25rem !important; } .ms-2 { margin-right: 0.5rem !important; } .ms-3 { margin-right: 1rem !important; } .ms-4 { margin-right: 1.5rem !important; } .ms-5 { margin-right: 3rem !important; } .ms-auto { margin-right: auto !important; } .p-0 { padding: 0 !important; } .p-1 { padding: 0.25rem !important; } .p-2 { padding: 0.5rem !important; } .p-3 { padding: 1rem !important; } .p-4 { padding: 1.5rem !important; } .p-5 { padding: 3rem !important; } .px-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-0 { padding-top: 0 !important; } .pt-1 { padding-top: 0.25rem !important; } .pt-2 { padding-top: 0.5rem !important; } .pt-3 { padding-top: 1rem !important; } .pt-4 { padding-top: 1.5rem !important; } .pt-5 { padding-top: 3rem !important; } .pe-0 { padding-left: 0 !important; } .pe-1 { padding-left: 0.25rem !important; } .pe-2 { padding-left: 0.5rem !important; } .pe-3 { padding-left: 1rem !important; } .pe-4 { padding-left: 1.5rem !important; } .pe-5 { padding-left: 3rem !important; } .pb-0 { padding-bottom: 0 !important; } .pb-1 { padding-bottom: 0.25rem !important; } .pb-2 { padding-bottom: 0.5rem !important; } .pb-3 { padding-bottom: 1rem !important; } .pb-4 { padding-bottom: 1.5rem !important; } .pb-5 { padding-bottom: 3rem !important; } .ps-0 { padding-right: 0 !important; } .ps-1 { padding-right: 0.25rem !important; } .ps-2 { padding-right: 0.5rem !important; } .ps-3 { padding-right: 1rem !important; } .ps-4 { padding-right: 1.5rem !important; } .ps-5 { padding-right: 3rem !important; } @media (min-width: 576px) { .d-sm-inline { display: inline !important; } .d-sm-inline-block { display: inline-block !important; } .d-sm-block { display: block !important; } .d-sm-grid { display: grid !important; } .d-sm-table { display: table !important; } .d-sm-table-row { display: table-row !important; } .d-sm-table-cell { display: table-cell !important; } .d-sm-flex { display: flex !important; } .d-sm-inline-flex { display: inline-flex !important; } .d-sm-none { display: none !important; } .flex-sm-fill { flex: 1 1 auto !important; } .flex-sm-row { flex-direction: row !important; } .flex-sm-column { flex-direction: column !important; } .flex-sm-row-reverse { flex-direction: row-reverse !important; } .flex-sm-column-reverse { flex-direction: column-reverse !important; } .flex-sm-grow-0 { flex-grow: 0 !important; } .flex-sm-grow-1 { flex-grow: 1 !important; } .flex-sm-shrink-0 { flex-shrink: 0 !important; } .flex-sm-shrink-1 { flex-shrink: 1 !important; } .flex-sm-wrap { flex-wrap: wrap !important; } .flex-sm-nowrap { flex-wrap: nowrap !important; } .flex-sm-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-sm-start { justify-content: flex-start !important; } .justify-content-sm-end { justify-content: flex-end !important; } .justify-content-sm-center { justify-content: center !important; } .justify-content-sm-between { justify-content: space-between !important; } .justify-content-sm-around { justify-content: space-around !important; } .justify-content-sm-evenly { justify-content: space-evenly !important; } .align-items-sm-start { align-items: flex-start !important; } .align-items-sm-end { align-items: flex-end !important; } .align-items-sm-center { align-items: center !important; } .align-items-sm-baseline { align-items: baseline !important; } .align-items-sm-stretch { align-items: stretch !important; } .align-content-sm-start { align-content: flex-start !important; } .align-content-sm-end { align-content: flex-end !important; } .align-content-sm-center { align-content: center !important; } .align-content-sm-between { align-content: space-between !important; } .align-content-sm-around { align-content: space-around !important; } .align-content-sm-stretch { align-content: stretch !important; } .align-self-sm-auto { align-self: auto !important; } .align-self-sm-start { align-self: flex-start !important; } .align-self-sm-end { align-self: flex-end !important; } .align-self-sm-center { align-self: center !important; } .align-self-sm-baseline { align-self: baseline !important; } .align-self-sm-stretch { align-self: stretch !important; } .order-sm-first { order: -1 !important; } .order-sm-0 { order: 0 !important; } .order-sm-1 { order: 1 !important; } .order-sm-2 { order: 2 !important; } .order-sm-3 { order: 3 !important; } .order-sm-4 { order: 4 !important; } .order-sm-5 { order: 5 !important; } .order-sm-last { order: 6 !important; } .m-sm-0 { margin: 0 !important; } .m-sm-1 { margin: 0.25rem !important; } .m-sm-2 { margin: 0.5rem !important; } .m-sm-3 { margin: 1rem !important; } .m-sm-4 { margin: 1.5rem !important; } .m-sm-5 { margin: 3rem !important; } .m-sm-auto { margin: auto !important; } .mx-sm-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-sm-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-sm-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-sm-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-sm-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-sm-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-sm-auto { margin-left: auto !important; margin-right: auto !important; } .my-sm-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-sm-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-sm-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-sm-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-sm-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-sm-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-sm-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-sm-0 { margin-top: 0 !important; } .mt-sm-1 { margin-top: 0.25rem !important; } .mt-sm-2 { margin-top: 0.5rem !important; } .mt-sm-3 { margin-top: 1rem !important; } .mt-sm-4 { margin-top: 1.5rem !important; } .mt-sm-5 { margin-top: 3rem !important; } .mt-sm-auto { margin-top: auto !important; } .me-sm-0 { margin-left: 0 !important; } .me-sm-1 { margin-left: 0.25rem !important; } .me-sm-2 { margin-left: 0.5rem !important; } .me-sm-3 { margin-left: 1rem !important; } .me-sm-4 { margin-left: 1.5rem !important; } .me-sm-5 { margin-left: 3rem !important; } .me-sm-auto { margin-left: auto !important; } .mb-sm-0 { margin-bottom: 0 !important; } .mb-sm-1 { margin-bottom: 0.25rem !important; } .mb-sm-2 { margin-bottom: 0.5rem !important; } .mb-sm-3 { margin-bottom: 1rem !important; } .mb-sm-4 { margin-bottom: 1.5rem !important; } .mb-sm-5 { margin-bottom: 3rem !important; } .mb-sm-auto { margin-bottom: auto !important; } .ms-sm-0 { margin-right: 0 !important; } .ms-sm-1 { margin-right: 0.25rem !important; } .ms-sm-2 { margin-right: 0.5rem !important; } .ms-sm-3 { margin-right: 1rem !important; } .ms-sm-4 { margin-right: 1.5rem !important; } .ms-sm-5 { margin-right: 3rem !important; } .ms-sm-auto { margin-right: auto !important; } .p-sm-0 { padding: 0 !important; } .p-sm-1 { padding: 0.25rem !important; } .p-sm-2 { padding: 0.5rem !important; } .p-sm-3 { padding: 1rem !important; } .p-sm-4 { padding: 1.5rem !important; } .p-sm-5 { padding: 3rem !important; } .px-sm-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-sm-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-sm-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-sm-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-sm-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-sm-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-sm-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-sm-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-sm-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-sm-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-sm-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-sm-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-sm-0 { padding-top: 0 !important; } .pt-sm-1 { padding-top: 0.25rem !important; } .pt-sm-2 { padding-top: 0.5rem !important; } .pt-sm-3 { padding-top: 1rem !important; } .pt-sm-4 { padding-top: 1.5rem !important; } .pt-sm-5 { padding-top: 3rem !important; } .pe-sm-0 { padding-left: 0 !important; } .pe-sm-1 { padding-left: 0.25rem !important; } .pe-sm-2 { padding-left: 0.5rem !important; } .pe-sm-3 { padding-left: 1rem !important; } .pe-sm-4 { padding-left: 1.5rem !important; } .pe-sm-5 { padding-left: 3rem !important; } .pb-sm-0 { padding-bottom: 0 !important; } .pb-sm-1 { padding-bottom: 0.25rem !important; } .pb-sm-2 { padding-bottom: 0.5rem !important; } .pb-sm-3 { padding-bottom: 1rem !important; } .pb-sm-4 { padding-bottom: 1.5rem !important; } .pb-sm-5 { padding-bottom: 3rem !important; } .ps-sm-0 { padding-right: 0 !important; } .ps-sm-1 { padding-right: 0.25rem !important; } .ps-sm-2 { padding-right: 0.5rem !important; } .ps-sm-3 { padding-right: 1rem !important; } .ps-sm-4 { padding-right: 1.5rem !important; } .ps-sm-5 { padding-right: 3rem !important; } } @media (min-width: 768px) { .d-md-inline { display: inline !important; } .d-md-inline-block { display: inline-block !important; } .d-md-block { display: block !important; } .d-md-grid { display: grid !important; } .d-md-table { display: table !important; } .d-md-table-row { display: table-row !important; } .d-md-table-cell { display: table-cell !important; } .d-md-flex { display: flex !important; } .d-md-inline-flex { display: inline-flex !important; } .d-md-none { display: none !important; } .flex-md-fill { flex: 1 1 auto !important; } .flex-md-row { flex-direction: row !important; } .flex-md-column { flex-direction: column !important; } .flex-md-row-reverse { flex-direction: row-reverse !important; } .flex-md-column-reverse { flex-direction: column-reverse !important; } .flex-md-grow-0 { flex-grow: 0 !important; } .flex-md-grow-1 { flex-grow: 1 !important; } .flex-md-shrink-0 { flex-shrink: 0 !important; } .flex-md-shrink-1 { flex-shrink: 1 !important; } .flex-md-wrap { flex-wrap: wrap !important; } .flex-md-nowrap { flex-wrap: nowrap !important; } .flex-md-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-md-start { justify-content: flex-start !important; } .justify-content-md-end { justify-content: flex-end !important; } .justify-content-md-center { justify-content: center !important; } .justify-content-md-between { justify-content: space-between !important; } .justify-content-md-around { justify-content: space-around !important; } .justify-content-md-evenly { justify-content: space-evenly !important; } .align-items-md-start { align-items: flex-start !important; } .align-items-md-end { align-items: flex-end !important; } .align-items-md-center { align-items: center !important; } .align-items-md-baseline { align-items: baseline !important; } .align-items-md-stretch { align-items: stretch !important; } .align-content-md-start { align-content: flex-start !important; } .align-content-md-end { align-content: flex-end !important; } .align-content-md-center { align-content: center !important; } .align-content-md-between { align-content: space-between !important; } .align-content-md-around { align-content: space-around !important; } .align-content-md-stretch { align-content: stretch !important; } .align-self-md-auto { align-self: auto !important; } .align-self-md-start { align-self: flex-start !important; } .align-self-md-end { align-self: flex-end !important; } .align-self-md-center { align-self: center !important; } .align-self-md-baseline { align-self: baseline !important; } .align-self-md-stretch { align-self: stretch !important; } .order-md-first { order: -1 !important; } .order-md-0 { order: 0 !important; } .order-md-1 { order: 1 !important; } .order-md-2 { order: 2 !important; } .order-md-3 { order: 3 !important; } .order-md-4 { order: 4 !important; } .order-md-5 { order: 5 !important; } .order-md-last { order: 6 !important; } .m-md-0 { margin: 0 !important; } .m-md-1 { margin: 0.25rem !important; } .m-md-2 { margin: 0.5rem !important; } .m-md-3 { margin: 1rem !important; } .m-md-4 { margin: 1.5rem !important; } .m-md-5 { margin: 3rem !important; } .m-md-auto { margin: auto !important; } .mx-md-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-md-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-md-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-md-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-md-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-md-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-md-auto { margin-left: auto !important; margin-right: auto !important; } .my-md-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-md-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-md-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-md-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-md-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-md-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-md-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-md-0 { margin-top: 0 !important; } .mt-md-1 { margin-top: 0.25rem !important; } .mt-md-2 { margin-top: 0.5rem !important; } .mt-md-3 { margin-top: 1rem !important; } .mt-md-4 { margin-top: 1.5rem !important; } .mt-md-5 { margin-top: 3rem !important; } .mt-md-auto { margin-top: auto !important; } .me-md-0 { margin-left: 0 !important; } .me-md-1 { margin-left: 0.25rem !important; } .me-md-2 { margin-left: 0.5rem !important; } .me-md-3 { margin-left: 1rem !important; } .me-md-4 { margin-left: 1.5rem !important; } .me-md-5 { margin-left: 3rem !important; } .me-md-auto { margin-left: auto !important; } .mb-md-0 { margin-bottom: 0 !important; } .mb-md-1 { margin-bottom: 0.25rem !important; } .mb-md-2 { margin-bottom: 0.5rem !important; } .mb-md-3 { margin-bottom: 1rem !important; } .mb-md-4 { margin-bottom: 1.5rem !important; } .mb-md-5 { margin-bottom: 3rem !important; } .mb-md-auto { margin-bottom: auto !important; } .ms-md-0 { margin-right: 0 !important; } .ms-md-1 { margin-right: 0.25rem !important; } .ms-md-2 { margin-right: 0.5rem !important; } .ms-md-3 { margin-right: 1rem !important; } .ms-md-4 { margin-right: 1.5rem !important; } .ms-md-5 { margin-right: 3rem !important; } .ms-md-auto { margin-right: auto !important; } .p-md-0 { padding: 0 !important; } .p-md-1 { padding: 0.25rem !important; } .p-md-2 { padding: 0.5rem !important; } .p-md-3 { padding: 1rem !important; } .p-md-4 { padding: 1.5rem !important; } .p-md-5 { padding: 3rem !important; } .px-md-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-md-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-md-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-md-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-md-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-md-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-md-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-md-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-md-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-md-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-md-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-md-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-md-0 { padding-top: 0 !important; } .pt-md-1 { padding-top: 0.25rem !important; } .pt-md-2 { padding-top: 0.5rem !important; } .pt-md-3 { padding-top: 1rem !important; } .pt-md-4 { padding-top: 1.5rem !important; } .pt-md-5 { padding-top: 3rem !important; } .pe-md-0 { padding-left: 0 !important; } .pe-md-1 { padding-left: 0.25rem !important; } .pe-md-2 { padding-left: 0.5rem !important; } .pe-md-3 { padding-left: 1rem !important; } .pe-md-4 { padding-left: 1.5rem !important; } .pe-md-5 { padding-left: 3rem !important; } .pb-md-0 { padding-bottom: 0 !important; } .pb-md-1 { padding-bottom: 0.25rem !important; } .pb-md-2 { padding-bottom: 0.5rem !important; } .pb-md-3 { padding-bottom: 1rem !important; } .pb-md-4 { padding-bottom: 1.5rem !important; } .pb-md-5 { padding-bottom: 3rem !important; } .ps-md-0 { padding-right: 0 !important; } .ps-md-1 { padding-right: 0.25rem !important; } .ps-md-2 { padding-right: 0.5rem !important; } .ps-md-3 { padding-right: 1rem !important; } .ps-md-4 { padding-right: 1.5rem !important; } .ps-md-5 { padding-right: 3rem !important; } } @media (min-width: 992px) { .d-lg-inline { display: inline !important; } .d-lg-inline-block { display: inline-block !important; } .d-lg-block { display: block !important; } .d-lg-grid { display: grid !important; } .d-lg-table { display: table !important; } .d-lg-table-row { display: table-row !important; } .d-lg-table-cell { display: table-cell !important; } .d-lg-flex { display: flex !important; } .d-lg-inline-flex { display: inline-flex !important; } .d-lg-none { display: none !important; } .flex-lg-fill { flex: 1 1 auto !important; } .flex-lg-row { flex-direction: row !important; } .flex-lg-column { flex-direction: column !important; } .flex-lg-row-reverse { flex-direction: row-reverse !important; } .flex-lg-column-reverse { flex-direction: column-reverse !important; } .flex-lg-grow-0 { flex-grow: 0 !important; } .flex-lg-grow-1 { flex-grow: 1 !important; } .flex-lg-shrink-0 { flex-shrink: 0 !important; } .flex-lg-shrink-1 { flex-shrink: 1 !important; } .flex-lg-wrap { flex-wrap: wrap !important; } .flex-lg-nowrap { flex-wrap: nowrap !important; } .flex-lg-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-lg-start { justify-content: flex-start !important; } .justify-content-lg-end { justify-content: flex-end !important; } .justify-content-lg-center { justify-content: center !important; } .justify-content-lg-between { justify-content: space-between !important; } .justify-content-lg-around { justify-content: space-around !important; } .justify-content-lg-evenly { justify-content: space-evenly !important; } .align-items-lg-start { align-items: flex-start !important; } .align-items-lg-end { align-items: flex-end !important; } .align-items-lg-center { align-items: center !important; } .align-items-lg-baseline { align-items: baseline !important; } .align-items-lg-stretch { align-items: stretch !important; } .align-content-lg-start { align-content: flex-start !important; } .align-content-lg-end { align-content: flex-end !important; } .align-content-lg-center { align-content: center !important; } .align-content-lg-between { align-content: space-between !important; } .align-content-lg-around { align-content: space-around !important; } .align-content-lg-stretch { align-content: stretch !important; } .align-self-lg-auto { align-self: auto !important; } .align-self-lg-start { align-self: flex-start !important; } .align-self-lg-end { align-self: flex-end !important; } .align-self-lg-center { align-self: center !important; } .align-self-lg-baseline { align-self: baseline !important; } .align-self-lg-stretch { align-self: stretch !important; } .order-lg-first { order: -1 !important; } .order-lg-0 { order: 0 !important; } .order-lg-1 { order: 1 !important; } .order-lg-2 { order: 2 !important; } .order-lg-3 { order: 3 !important; } .order-lg-4 { order: 4 !important; } .order-lg-5 { order: 5 !important; } .order-lg-last { order: 6 !important; } .m-lg-0 { margin: 0 !important; } .m-lg-1 { margin: 0.25rem !important; } .m-lg-2 { margin: 0.5rem !important; } .m-lg-3 { margin: 1rem !important; } .m-lg-4 { margin: 1.5rem !important; } .m-lg-5 { margin: 3rem !important; } .m-lg-auto { margin: auto !important; } .mx-lg-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-lg-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-lg-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-lg-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-lg-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-lg-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-lg-auto { margin-left: auto !important; margin-right: auto !important; } .my-lg-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-lg-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-lg-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-lg-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-lg-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-lg-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-lg-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-lg-0 { margin-top: 0 !important; } .mt-lg-1 { margin-top: 0.25rem !important; } .mt-lg-2 { margin-top: 0.5rem !important; } .mt-lg-3 { margin-top: 1rem !important; } .mt-lg-4 { margin-top: 1.5rem !important; } .mt-lg-5 { margin-top: 3rem !important; } .mt-lg-auto { margin-top: auto !important; } .me-lg-0 { margin-left: 0 !important; } .me-lg-1 { margin-left: 0.25rem !important; } .me-lg-2 { margin-left: 0.5rem !important; } .me-lg-3 { margin-left: 1rem !important; } .me-lg-4 { margin-left: 1.5rem !important; } .me-lg-5 { margin-left: 3rem !important; } .me-lg-auto { margin-left: auto !important; } .mb-lg-0 { margin-bottom: 0 !important; } .mb-lg-1 { margin-bottom: 0.25rem !important; } .mb-lg-2 { margin-bottom: 0.5rem !important; } .mb-lg-3 { margin-bottom: 1rem !important; } .mb-lg-4 { margin-bottom: 1.5rem !important; } .mb-lg-5 { margin-bottom: 3rem !important; } .mb-lg-auto { margin-bottom: auto !important; } .ms-lg-0 { margin-right: 0 !important; } .ms-lg-1 { margin-right: 0.25rem !important; } .ms-lg-2 { margin-right: 0.5rem !important; } .ms-lg-3 { margin-right: 1rem !important; } .ms-lg-4 { margin-right: 1.5rem !important; } .ms-lg-5 { margin-right: 3rem !important; } .ms-lg-auto { margin-right: auto !important; } .p-lg-0 { padding: 0 !important; } .p-lg-1 { padding: 0.25rem !important; } .p-lg-2 { padding: 0.5rem !important; } .p-lg-3 { padding: 1rem !important; } .p-lg-4 { padding: 1.5rem !important; } .p-lg-5 { padding: 3rem !important; } .px-lg-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-lg-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-lg-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-lg-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-lg-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-lg-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-lg-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-lg-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-lg-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-lg-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-lg-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-lg-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-lg-0 { padding-top: 0 !important; } .pt-lg-1 { padding-top: 0.25rem !important; } .pt-lg-2 { padding-top: 0.5rem !important; } .pt-lg-3 { padding-top: 1rem !important; } .pt-lg-4 { padding-top: 1.5rem !important; } .pt-lg-5 { padding-top: 3rem !important; } .pe-lg-0 { padding-left: 0 !important; } .pe-lg-1 { padding-left: 0.25rem !important; } .pe-lg-2 { padding-left: 0.5rem !important; } .pe-lg-3 { padding-left: 1rem !important; } .pe-lg-4 { padding-left: 1.5rem !important; } .pe-lg-5 { padding-left: 3rem !important; } .pb-lg-0 { padding-bottom: 0 !important; } .pb-lg-1 { padding-bottom: 0.25rem !important; } .pb-lg-2 { padding-bottom: 0.5rem !important; } .pb-lg-3 { padding-bottom: 1rem !important; } .pb-lg-4 { padding-bottom: 1.5rem !important; } .pb-lg-5 { padding-bottom: 3rem !important; } .ps-lg-0 { padding-right: 0 !important; } .ps-lg-1 { padding-right: 0.25rem !important; } .ps-lg-2 { padding-right: 0.5rem !important; } .ps-lg-3 { padding-right: 1rem !important; } .ps-lg-4 { padding-right: 1.5rem !important; } .ps-lg-5 { padding-right: 3rem !important; } } @media (min-width: 1200px) { .d-xl-inline { display: inline !important; } .d-xl-inline-block { display: inline-block !important; } .d-xl-block { display: block !important; } .d-xl-grid { display: grid !important; } .d-xl-table { display: table !important; } .d-xl-table-row { display: table-row !important; } .d-xl-table-cell { display: table-cell !important; } .d-xl-flex { display: flex !important; } .d-xl-inline-flex { display: inline-flex !important; } .d-xl-none { display: none !important; } .flex-xl-fill { flex: 1 1 auto !important; } .flex-xl-row { flex-direction: row !important; } .flex-xl-column { flex-direction: column !important; } .flex-xl-row-reverse { flex-direction: row-reverse !important; } .flex-xl-column-reverse { flex-direction: column-reverse !important; } .flex-xl-grow-0 { flex-grow: 0 !important; } .flex-xl-grow-1 { flex-grow: 1 !important; } .flex-xl-shrink-0 { flex-shrink: 0 !important; } .flex-xl-shrink-1 { flex-shrink: 1 !important; } .flex-xl-wrap { flex-wrap: wrap !important; } .flex-xl-nowrap { flex-wrap: nowrap !important; } .flex-xl-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-xl-start { justify-content: flex-start !important; } .justify-content-xl-end { justify-content: flex-end !important; } .justify-content-xl-center { justify-content: center !important; } .justify-content-xl-between { justify-content: space-between !important; } .justify-content-xl-around { justify-content: space-around !important; } .justify-content-xl-evenly { justify-content: space-evenly !important; } .align-items-xl-start { align-items: flex-start !important; } .align-items-xl-end { align-items: flex-end !important; } .align-items-xl-center { align-items: center !important; } .align-items-xl-baseline { align-items: baseline !important; } .align-items-xl-stretch { align-items: stretch !important; } .align-content-xl-start { align-content: flex-start !important; } .align-content-xl-end { align-content: flex-end !important; } .align-content-xl-center { align-content: center !important; } .align-content-xl-between { align-content: space-between !important; } .align-content-xl-around { align-content: space-around !important; } .align-content-xl-stretch { align-content: stretch !important; } .align-self-xl-auto { align-self: auto !important; } .align-self-xl-start { align-self: flex-start !important; } .align-self-xl-end { align-self: flex-end !important; } .align-self-xl-center { align-self: center !important; } .align-self-xl-baseline { align-self: baseline !important; } .align-self-xl-stretch { align-self: stretch !important; } .order-xl-first { order: -1 !important; } .order-xl-0 { order: 0 !important; } .order-xl-1 { order: 1 !important; } .order-xl-2 { order: 2 !important; } .order-xl-3 { order: 3 !important; } .order-xl-4 { order: 4 !important; } .order-xl-5 { order: 5 !important; } .order-xl-last { order: 6 !important; } .m-xl-0 { margin: 0 !important; } .m-xl-1 { margin: 0.25rem !important; } .m-xl-2 { margin: 0.5rem !important; } .m-xl-3 { margin: 1rem !important; } .m-xl-4 { margin: 1.5rem !important; } .m-xl-5 { margin: 3rem !important; } .m-xl-auto { margin: auto !important; } .mx-xl-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-xl-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-xl-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-xl-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-xl-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-xl-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-xl-auto { margin-left: auto !important; margin-right: auto !important; } .my-xl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xl-0 { margin-top: 0 !important; } .mt-xl-1 { margin-top: 0.25rem !important; } .mt-xl-2 { margin-top: 0.5rem !important; } .mt-xl-3 { margin-top: 1rem !important; } .mt-xl-4 { margin-top: 1.5rem !important; } .mt-xl-5 { margin-top: 3rem !important; } .mt-xl-auto { margin-top: auto !important; } .me-xl-0 { margin-left: 0 !important; } .me-xl-1 { margin-left: 0.25rem !important; } .me-xl-2 { margin-left: 0.5rem !important; } .me-xl-3 { margin-left: 1rem !important; } .me-xl-4 { margin-left: 1.5rem !important; } .me-xl-5 { margin-left: 3rem !important; } .me-xl-auto { margin-left: auto !important; } .mb-xl-0 { margin-bottom: 0 !important; } .mb-xl-1 { margin-bottom: 0.25rem !important; } .mb-xl-2 { margin-bottom: 0.5rem !important; } .mb-xl-3 { margin-bottom: 1rem !important; } .mb-xl-4 { margin-bottom: 1.5rem !important; } .mb-xl-5 { margin-bottom: 3rem !important; } .mb-xl-auto { margin-bottom: auto !important; } .ms-xl-0 { margin-right: 0 !important; } .ms-xl-1 { margin-right: 0.25rem !important; } .ms-xl-2 { margin-right: 0.5rem !important; } .ms-xl-3 { margin-right: 1rem !important; } .ms-xl-4 { margin-right: 1.5rem !important; } .ms-xl-5 { margin-right: 3rem !important; } .ms-xl-auto { margin-right: auto !important; } .p-xl-0 { padding: 0 !important; } .p-xl-1 { padding: 0.25rem !important; } .p-xl-2 { padding: 0.5rem !important; } .p-xl-3 { padding: 1rem !important; } .p-xl-4 { padding: 1.5rem !important; } .p-xl-5 { padding: 3rem !important; } .px-xl-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-xl-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-xl-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-xl-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-xl-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-xl-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-xl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xl-0 { padding-top: 0 !important; } .pt-xl-1 { padding-top: 0.25rem !important; } .pt-xl-2 { padding-top: 0.5rem !important; } .pt-xl-3 { padding-top: 1rem !important; } .pt-xl-4 { padding-top: 1.5rem !important; } .pt-xl-5 { padding-top: 3rem !important; } .pe-xl-0 { padding-left: 0 !important; } .pe-xl-1 { padding-left: 0.25rem !important; } .pe-xl-2 { padding-left: 0.5rem !important; } .pe-xl-3 { padding-left: 1rem !important; } .pe-xl-4 { padding-left: 1.5rem !important; } .pe-xl-5 { padding-left: 3rem !important; } .pb-xl-0 { padding-bottom: 0 !important; } .pb-xl-1 { padding-bottom: 0.25rem !important; } .pb-xl-2 { padding-bottom: 0.5rem !important; } .pb-xl-3 { padding-bottom: 1rem !important; } .pb-xl-4 { padding-bottom: 1.5rem !important; } .pb-xl-5 { padding-bottom: 3rem !important; } .ps-xl-0 { padding-right: 0 !important; } .ps-xl-1 { padding-right: 0.25rem !important; } .ps-xl-2 { padding-right: 0.5rem !important; } .ps-xl-3 { padding-right: 1rem !important; } .ps-xl-4 { padding-right: 1.5rem !important; } .ps-xl-5 { padding-right: 3rem !important; } } @media (min-width: 1400px) { .d-xxl-inline { display: inline !important; } .d-xxl-inline-block { display: inline-block !important; } .d-xxl-block { display: block !important; } .d-xxl-grid { display: grid !important; } .d-xxl-table { display: table !important; } .d-xxl-table-row { display: table-row !important; } .d-xxl-table-cell { display: table-cell !important; } .d-xxl-flex { display: flex !important; } .d-xxl-inline-flex { display: inline-flex !important; } .d-xxl-none { display: none !important; } .flex-xxl-fill { flex: 1 1 auto !important; } .flex-xxl-row { flex-direction: row !important; } .flex-xxl-column { flex-direction: column !important; } .flex-xxl-row-reverse { flex-direction: row-reverse !important; } .flex-xxl-column-reverse { flex-direction: column-reverse !important; } .flex-xxl-grow-0 { flex-grow: 0 !important; } .flex-xxl-grow-1 { flex-grow: 1 !important; } .flex-xxl-shrink-0 { flex-shrink: 0 !important; } .flex-xxl-shrink-1 { flex-shrink: 1 !important; } .flex-xxl-wrap { flex-wrap: wrap !important; } .flex-xxl-nowrap { flex-wrap: nowrap !important; } .flex-xxl-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-xxl-start { justify-content: flex-start !important; } .justify-content-xxl-end { justify-content: flex-end !important; } .justify-content-xxl-center { justify-content: center !important; } .justify-content-xxl-between { justify-content: space-between !important; } .justify-content-xxl-around { justify-content: space-around !important; } .justify-content-xxl-evenly { justify-content: space-evenly !important; } .align-items-xxl-start { align-items: flex-start !important; } .align-items-xxl-end { align-items: flex-end !important; } .align-items-xxl-center { align-items: center !important; } .align-items-xxl-baseline { align-items: baseline !important; } .align-items-xxl-stretch { align-items: stretch !important; } .align-content-xxl-start { align-content: flex-start !important; } .align-content-xxl-end { align-content: flex-end !important; } .align-content-xxl-center { align-content: center !important; } .align-content-xxl-between { align-content: space-between !important; } .align-content-xxl-around { align-content: space-around !important; } .align-content-xxl-stretch { align-content: stretch !important; } .align-self-xxl-auto { align-self: auto !important; } .align-self-xxl-start { align-self: flex-start !important; } .align-self-xxl-end { align-self: flex-end !important; } .align-self-xxl-center { align-self: center !important; } .align-self-xxl-baseline { align-self: baseline !important; } .align-self-xxl-stretch { align-self: stretch !important; } .order-xxl-first { order: -1 !important; } .order-xxl-0 { order: 0 !important; } .order-xxl-1 { order: 1 !important; } .order-xxl-2 { order: 2 !important; } .order-xxl-3 { order: 3 !important; } .order-xxl-4 { order: 4 !important; } .order-xxl-5 { order: 5 !important; } .order-xxl-last { order: 6 !important; } .m-xxl-0 { margin: 0 !important; } .m-xxl-1 { margin: 0.25rem !important; } .m-xxl-2 { margin: 0.5rem !important; } .m-xxl-3 { margin: 1rem !important; } .m-xxl-4 { margin: 1.5rem !important; } .m-xxl-5 { margin: 3rem !important; } .m-xxl-auto { margin: auto !important; } .mx-xxl-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-xxl-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-xxl-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-xxl-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-xxl-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-xxl-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-xxl-auto { margin-left: auto !important; margin-right: auto !important; } .my-xxl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xxl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xxl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xxl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xxl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xxl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xxl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xxl-0 { margin-top: 0 !important; } .mt-xxl-1 { margin-top: 0.25rem !important; } .mt-xxl-2 { margin-top: 0.5rem !important; } .mt-xxl-3 { margin-top: 1rem !important; } .mt-xxl-4 { margin-top: 1.5rem !important; } .mt-xxl-5 { margin-top: 3rem !important; } .mt-xxl-auto { margin-top: auto !important; } .me-xxl-0 { margin-left: 0 !important; } .me-xxl-1 { margin-left: 0.25rem !important; } .me-xxl-2 { margin-left: 0.5rem !important; } .me-xxl-3 { margin-left: 1rem !important; } .me-xxl-4 { margin-left: 1.5rem !important; } .me-xxl-5 { margin-left: 3rem !important; } .me-xxl-auto { margin-left: auto !important; } .mb-xxl-0 { margin-bottom: 0 !important; } .mb-xxl-1 { margin-bottom: 0.25rem !important; } .mb-xxl-2 { margin-bottom: 0.5rem !important; } .mb-xxl-3 { margin-bottom: 1rem !important; } .mb-xxl-4 { margin-bottom: 1.5rem !important; } .mb-xxl-5 { margin-bottom: 3rem !important; } .mb-xxl-auto { margin-bottom: auto !important; } .ms-xxl-0 { margin-right: 0 !important; } .ms-xxl-1 { margin-right: 0.25rem !important; } .ms-xxl-2 { margin-right: 0.5rem !important; } .ms-xxl-3 { margin-right: 1rem !important; } .ms-xxl-4 { margin-right: 1.5rem !important; } .ms-xxl-5 { margin-right: 3rem !important; } .ms-xxl-auto { margin-right: auto !important; } .p-xxl-0 { padding: 0 !important; } .p-xxl-1 { padding: 0.25rem !important; } .p-xxl-2 { padding: 0.5rem !important; } .p-xxl-3 { padding: 1rem !important; } .p-xxl-4 { padding: 1.5rem !important; } .p-xxl-5 { padding: 3rem !important; } .px-xxl-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-xxl-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-xxl-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-xxl-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-xxl-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-xxl-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-xxl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xxl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xxl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xxl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xxl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xxl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xxl-0 { padding-top: 0 !important; } .pt-xxl-1 { padding-top: 0.25rem !important; } .pt-xxl-2 { padding-top: 0.5rem !important; } .pt-xxl-3 { padding-top: 1rem !important; } .pt-xxl-4 { padding-top: 1.5rem !important; } .pt-xxl-5 { padding-top: 3rem !important; } .pe-xxl-0 { padding-left: 0 !important; } .pe-xxl-1 { padding-left: 0.25rem !important; } .pe-xxl-2 { padding-left: 0.5rem !important; } .pe-xxl-3 { padding-left: 1rem !important; } .pe-xxl-4 { padding-left: 1.5rem !important; } .pe-xxl-5 { padding-left: 3rem !important; } .pb-xxl-0 { padding-bottom: 0 !important; } .pb-xxl-1 { padding-bottom: 0.25rem !important; } .pb-xxl-2 { padding-bottom: 0.5rem !important; } .pb-xxl-3 { padding-bottom: 1rem !important; } .pb-xxl-4 { padding-bottom: 1.5rem !important; } .pb-xxl-5 { padding-bottom: 3rem !important; } .ps-xxl-0 { padding-right: 0 !important; } .ps-xxl-1 { padding-right: 0.25rem !important; } .ps-xxl-2 { padding-right: 0.5rem !important; } .ps-xxl-3 { padding-right: 1rem !important; } .ps-xxl-4 { padding-right: 1.5rem !important; } .ps-xxl-5 { padding-right: 3rem !important; } } @media print { .d-print-inline { display: inline !important; } .d-print-inline-block { display: inline-block !important; } .d-print-block { display: block !important; } .d-print-grid { display: grid !important; } .d-print-table { display: table !important; } .d-print-table-row { display: table-row !important; } .d-print-table-cell { display: table-cell !important; } .d-print-flex { display: flex !important; } .d-print-inline-flex { display: inline-flex !important; } .d-print-none { display: none !important; } } /*# sourceMappingURL=bootstrap-grid.rtl.css.map */ ================================================ FILE: examples/tailwind-and-bootstrap-example/css-modules/bootstrap/bootstrap-reboot.css ================================================ /*! * Bootstrap Reboot v5.1.3 (https://getbootstrap.com/) * Copyright 2011-2021 The Bootstrap Authors * Copyright 2011-2021 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) */ :root { --bs-blue: #0d6efd; --bs-indigo: #6610f2; --bs-purple: #6f42c1; --bs-pink: #d63384; --bs-red: #dc3545; --bs-orange: #fd7e14; --bs-yellow: #ffc107; --bs-green: #198754; --bs-teal: #20c997; --bs-cyan: #0dcaf0; --bs-white: #fff; --bs-gray: #6c757d; --bs-gray-dark: #343a40; --bs-gray-100: #f8f9fa; --bs-gray-200: #e9ecef; --bs-gray-300: #dee2e6; --bs-gray-400: #ced4da; --bs-gray-500: #adb5bd; --bs-gray-600: #6c757d; --bs-gray-700: #495057; --bs-gray-800: #343a40; --bs-gray-900: #212529; --bs-primary: #0d6efd; --bs-secondary: #6c757d; --bs-success: #198754; --bs-info: #0dcaf0; --bs-warning: #ffc107; --bs-danger: #dc3545; --bs-light: #f8f9fa; --bs-dark: #212529; --bs-primary-rgb: 13, 110, 253; --bs-secondary-rgb: 108, 117, 125; --bs-success-rgb: 25, 135, 84; --bs-info-rgb: 13, 202, 240; --bs-warning-rgb: 255, 193, 7; --bs-danger-rgb: 220, 53, 69; --bs-light-rgb: 248, 249, 250; --bs-dark-rgb: 33, 37, 41; --bs-white-rgb: 255, 255, 255; --bs-black-rgb: 0, 0, 0; --bs-body-color-rgb: 33, 37, 41; --bs-body-bg-rgb: 255, 255, 255; --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --bs-gradient: linear-gradient( 180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0) ); --bs-body-font-family: var(--bs-font-sans-serif); --bs-body-font-size: 1rem; --bs-body-font-weight: 400; --bs-body-line-height: 1.5; --bs-body-color: #212529; --bs-body-bg: #fff; } *, *::before, *::after { box-sizing: border-box; } @media (prefers-reduced-motion: no-preference) { :root { scroll-behavior: smooth; } } body { margin: 0; font-family: var(--bs-body-font-family); font-size: var(--bs-body-font-size); font-weight: var(--bs-body-font-weight); line-height: var(--bs-body-line-height); color: var(--bs-body-color); text-align: var(--bs-body-text-align); background-color: var(--bs-body-bg); -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } hr { margin: 1rem 0; color: inherit; background-color: currentColor; border: 0; opacity: 0.25; } hr:not([size]) { height: 1px; } h6, h5, h4, h3, h2, h1 { margin-top: 0; margin-bottom: 0.5rem; font-weight: 500; line-height: 1.2; } h1 { font-size: calc(1.375rem + 1.5vw); } @media (min-width: 1200px) { h1 { font-size: 2.5rem; } } h2 { font-size: calc(1.325rem + 0.9vw); } @media (min-width: 1200px) { h2 { font-size: 2rem; } } h3 { font-size: calc(1.3rem + 0.6vw); } @media (min-width: 1200px) { h3 { font-size: 1.75rem; } } h4 { font-size: calc(1.275rem + 0.3vw); } @media (min-width: 1200px) { h4 { font-size: 1.5rem; } } h5 { font-size: 1.25rem; } h6 { font-size: 1rem; } p { margin-top: 0; margin-bottom: 1rem; } abbr[title], abbr[data-bs-original-title] { -webkit-text-decoration: underline dotted; text-decoration: underline dotted; cursor: help; -webkit-text-decoration-skip-ink: none; text-decoration-skip-ink: none; } address { margin-bottom: 1rem; font-style: normal; line-height: inherit; } ol, ul { padding-left: 2rem; } ol, ul, dl { margin-top: 0; margin-bottom: 1rem; } ol ol, ul ul, ol ul, ul ol { margin-bottom: 0; } dt { font-weight: 700; } dd { margin-bottom: 0.5rem; margin-left: 0; } blockquote { margin: 0 0 1rem; } b, strong { font-weight: bolder; } small { font-size: 0.875em; } mark { padding: 0.2em; background-color: #fcf8e3; } sub, sup { position: relative; font-size: 0.75em; line-height: 0; vertical-align: baseline; } sub { bottom: -0.25em; } sup { top: -0.5em; } a { color: #0d6efd; text-decoration: underline; } a:hover { color: #0a58ca; } a:not([href]):not([class]), a:not([href]):not([class]):hover { color: inherit; text-decoration: none; } pre, code, kbd, samp { font-family: var(--bs-font-monospace); font-size: 1em; direction: ltr /* rtl:ignore */; unicode-bidi: bidi-override; } pre { display: block; margin-top: 0; margin-bottom: 1rem; overflow: auto; font-size: 0.875em; } pre code { font-size: inherit; color: inherit; word-break: normal; } code { font-size: 0.875em; color: #d63384; word-wrap: break-word; } a > code { color: inherit; } kbd { padding: 0.2rem 0.4rem; font-size: 0.875em; color: #fff; background-color: #212529; border-radius: 0.2rem; } kbd kbd { padding: 0; font-size: 1em; font-weight: 700; } figure { margin: 0 0 1rem; } img, svg { vertical-align: middle; } table { caption-side: bottom; border-collapse: collapse; } caption { padding-top: 0.5rem; padding-bottom: 0.5rem; color: #6c757d; text-align: left; } th { text-align: inherit; text-align: -webkit-match-parent; } thead, tbody, tfoot, tr, td, th { border-color: inherit; border-style: solid; border-width: 0; } label { display: inline-block; } button { border-radius: 0; } button:focus:not(:focus-visible) { outline: 0; } input, button, select, optgroup, textarea { margin: 0; font-family: inherit; font-size: inherit; line-height: inherit; } button, select { text-transform: none; } [role="button"] { cursor: pointer; } select { word-wrap: normal; } select:disabled { opacity: 1; } [list]::-webkit-calendar-picker-indicator { display: none; } button, [type="button"], [type="reset"], [type="submit"] { -webkit-appearance: button; } button:not(:disabled), [type="button"]:not(:disabled), [type="reset"]:not(:disabled), [type="submit"]:not(:disabled) { cursor: pointer; } ::-moz-focus-inner { padding: 0; border-style: none; } textarea { resize: vertical; } fieldset { min-width: 0; padding: 0; margin: 0; border: 0; } legend { float: left; width: 100%; padding: 0; margin-bottom: 0.5rem; font-size: calc(1.275rem + 0.3vw); line-height: inherit; } @media (min-width: 1200px) { legend { font-size: 1.5rem; } } legend + * { clear: left; } ::-webkit-datetime-edit-fields-wrapper, ::-webkit-datetime-edit-text, ::-webkit-datetime-edit-minute, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-year-field { padding: 0; } ::-webkit-inner-spin-button { height: auto; } [type="search"] { outline-offset: -2px; -webkit-appearance: textfield; } /* rtl:raw: [type="tel"], [type="url"], [type="email"], [type="number"] { direction: ltr; } */ ::-webkit-search-decoration { -webkit-appearance: none; } ::-webkit-color-swatch-wrapper { padding: 0; } ::-webkit-file-upload-button { font: inherit; } ::file-selector-button { font: inherit; } ::-webkit-file-upload-button { font: inherit; -webkit-appearance: button; } output { display: inline-block; } iframe { border: 0; } summary { display: list-item; cursor: pointer; } progress { vertical-align: baseline; } [hidden] { display: none !important; } /*# sourceMappingURL=bootstrap-reboot.css.map */ ================================================ FILE: examples/tailwind-and-bootstrap-example/css-modules/bootstrap/bootstrap-reboot.rtl.css ================================================ /*! * Bootstrap Reboot v5.1.3 (https://getbootstrap.com/) * Copyright 2011-2021 The Bootstrap Authors * Copyright 2011-2021 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) */ :root { --bs-blue: #0d6efd; --bs-indigo: #6610f2; --bs-purple: #6f42c1; --bs-pink: #d63384; --bs-red: #dc3545; --bs-orange: #fd7e14; --bs-yellow: #ffc107; --bs-green: #198754; --bs-teal: #20c997; --bs-cyan: #0dcaf0; --bs-white: #fff; --bs-gray: #6c757d; --bs-gray-dark: #343a40; --bs-gray-100: #f8f9fa; --bs-gray-200: #e9ecef; --bs-gray-300: #dee2e6; --bs-gray-400: #ced4da; --bs-gray-500: #adb5bd; --bs-gray-600: #6c757d; --bs-gray-700: #495057; --bs-gray-800: #343a40; --bs-gray-900: #212529; --bs-primary: #0d6efd; --bs-secondary: #6c757d; --bs-success: #198754; --bs-info: #0dcaf0; --bs-warning: #ffc107; --bs-danger: #dc3545; --bs-light: #f8f9fa; --bs-dark: #212529; --bs-primary-rgb: 13, 110, 253; --bs-secondary-rgb: 108, 117, 125; --bs-success-rgb: 25, 135, 84; --bs-info-rgb: 13, 202, 240; --bs-warning-rgb: 255, 193, 7; --bs-danger-rgb: 220, 53, 69; --bs-light-rgb: 248, 249, 250; --bs-dark-rgb: 33, 37, 41; --bs-white-rgb: 255, 255, 255; --bs-black-rgb: 0, 0, 0; --bs-body-color-rgb: 33, 37, 41; --bs-body-bg-rgb: 255, 255, 255; --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --bs-gradient: linear-gradient( 180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0) ); --bs-body-font-family: var(--bs-font-sans-serif); --bs-body-font-size: 1rem; --bs-body-font-weight: 400; --bs-body-line-height: 1.5; --bs-body-color: #212529; --bs-body-bg: #fff; } *, *::before, *::after { box-sizing: border-box; } @media (prefers-reduced-motion: no-preference) { :root { scroll-behavior: smooth; } } body { margin: 0; font-family: var(--bs-body-font-family); font-size: var(--bs-body-font-size); font-weight: var(--bs-body-font-weight); line-height: var(--bs-body-line-height); color: var(--bs-body-color); text-align: var(--bs-body-text-align); background-color: var(--bs-body-bg); -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } hr { margin: 1rem 0; color: inherit; background-color: currentColor; border: 0; opacity: 0.25; } hr:not([size]) { height: 1px; } h6, h5, h4, h3, h2, h1 { margin-top: 0; margin-bottom: 0.5rem; font-weight: 500; line-height: 1.2; } h1 { font-size: calc(1.375rem + 1.5vw); } @media (min-width: 1200px) { h1 { font-size: 2.5rem; } } h2 { font-size: calc(1.325rem + 0.9vw); } @media (min-width: 1200px) { h2 { font-size: 2rem; } } h3 { font-size: calc(1.3rem + 0.6vw); } @media (min-width: 1200px) { h3 { font-size: 1.75rem; } } h4 { font-size: calc(1.275rem + 0.3vw); } @media (min-width: 1200px) { h4 { font-size: 1.5rem; } } h5 { font-size: 1.25rem; } h6 { font-size: 1rem; } p { margin-top: 0; margin-bottom: 1rem; } abbr[title], abbr[data-bs-original-title] { -webkit-text-decoration: underline dotted; text-decoration: underline dotted; cursor: help; -webkit-text-decoration-skip-ink: none; text-decoration-skip-ink: none; } address { margin-bottom: 1rem; font-style: normal; line-height: inherit; } ol, ul { padding-right: 2rem; } ol, ul, dl { margin-top: 0; margin-bottom: 1rem; } ol ol, ul ul, ol ul, ul ol { margin-bottom: 0; } dt { font-weight: 700; } dd { margin-bottom: 0.5rem; margin-right: 0; } blockquote { margin: 0 0 1rem; } b, strong { font-weight: bolder; } small { font-size: 0.875em; } mark { padding: 0.2em; background-color: #fcf8e3; } sub, sup { position: relative; font-size: 0.75em; line-height: 0; vertical-align: baseline; } sub { bottom: -0.25em; } sup { top: -0.5em; } a { color: #0d6efd; text-decoration: underline; } a:hover { color: #0a58ca; } a:not([href]):not([class]), a:not([href]):not([class]):hover { color: inherit; text-decoration: none; } pre, code, kbd, samp { font-family: var(--bs-font-monospace); font-size: 1em; direction: ltr; unicode-bidi: bidi-override; } pre { display: block; margin-top: 0; margin-bottom: 1rem; overflow: auto; font-size: 0.875em; } pre code { font-size: inherit; color: inherit; word-break: normal; } code { font-size: 0.875em; color: #d63384; word-wrap: break-word; } a > code { color: inherit; } kbd { padding: 0.2rem 0.4rem; font-size: 0.875em; color: #fff; background-color: #212529; border-radius: 0.2rem; } kbd kbd { padding: 0; font-size: 1em; font-weight: 700; } figure { margin: 0 0 1rem; } img, svg { vertical-align: middle; } table { caption-side: bottom; border-collapse: collapse; } caption { padding-top: 0.5rem; padding-bottom: 0.5rem; color: #6c757d; text-align: right; } th { text-align: inherit; text-align: -webkit-match-parent; } thead, tbody, tfoot, tr, td, th { border-color: inherit; border-style: solid; border-width: 0; } label { display: inline-block; } button { border-radius: 0; } button:focus:not(:focus-visible) { outline: 0; } input, button, select, optgroup, textarea { margin: 0; font-family: inherit; font-size: inherit; line-height: inherit; } button, select { text-transform: none; } [role="button"] { cursor: pointer; } select { word-wrap: normal; } select:disabled { opacity: 1; } [list]::-webkit-calendar-picker-indicator { display: none; } button, [type="button"], [type="reset"], [type="submit"] { -webkit-appearance: button; } button:not(:disabled), [type="button"]:not(:disabled), [type="reset"]:not(:disabled), [type="submit"]:not(:disabled) { cursor: pointer; } ::-moz-focus-inner { padding: 0; border-style: none; } textarea { resize: vertical; } fieldset { min-width: 0; padding: 0; margin: 0; border: 0; } legend { float: right; width: 100%; padding: 0; margin-bottom: 0.5rem; font-size: calc(1.275rem + 0.3vw); line-height: inherit; } @media (min-width: 1200px) { legend { font-size: 1.5rem; } } legend + * { clear: right; } ::-webkit-datetime-edit-fields-wrapper, ::-webkit-datetime-edit-text, ::-webkit-datetime-edit-minute, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-year-field { padding: 0; } ::-webkit-inner-spin-button { height: auto; } [type="search"] { outline-offset: -2px; -webkit-appearance: textfield; } [type="tel"], [type="url"], [type="email"], [type="number"] { direction: ltr; } ::-webkit-search-decoration { -webkit-appearance: none; } ::-webkit-color-swatch-wrapper { padding: 0; } ::-webkit-file-upload-button { font: inherit; } ::file-selector-button { font: inherit; } ::-webkit-file-upload-button { font: inherit; -webkit-appearance: button; } output { display: inline-block; } iframe { border: 0; } summary { display: list-item; cursor: pointer; } progress { vertical-align: baseline; } [hidden] { display: none !important; } /*# sourceMappingURL=bootstrap-reboot.rtl.css.map */ ================================================ FILE: examples/tailwind-and-bootstrap-example/css-modules/bootstrap/bootstrap-utilities.css ================================================ /*! * Bootstrap Utilities v5.1.3 (https://getbootstrap.com/) * Copyright 2011-2021 The Bootstrap Authors * Copyright 2011-2021 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ .clearfix::after { display: block; clear: both; content: ""; } .link-primary { color: #0d6efd; } .link-primary:hover, .link-primary:focus { color: #0a58ca; } .link-secondary { color: #6c757d; } .link-secondary:hover, .link-secondary:focus { color: #565e64; } .link-success { color: #198754; } .link-success:hover, .link-success:focus { color: #146c43; } .link-info { color: #0dcaf0; } .link-info:hover, .link-info:focus { color: #3dd5f3; } .link-warning { color: #ffc107; } .link-warning:hover, .link-warning:focus { color: #ffcd39; } .link-danger { color: #dc3545; } .link-danger:hover, .link-danger:focus { color: #b02a37; } .link-light { color: #f8f9fa; } .link-light:hover, .link-light:focus { color: #f9fafb; } .link-dark { color: #212529; } .link-dark:hover, .link-dark:focus { color: #1a1e21; } .ratio { position: relative; width: 100%; } .ratio::before { display: block; padding-top: var(--bs-aspect-ratio); content: ""; } .ratio > * { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .ratio-1x1 { --bs-aspect-ratio: 100%; } .ratio-4x3 { --bs-aspect-ratio: 75%; } .ratio-16x9 { --bs-aspect-ratio: 56.25%; } .ratio-21x9 { --bs-aspect-ratio: 42.8571428571%; } .fixed-top { position: fixed; top: 0; right: 0; left: 0; z-index: 1030; } .fixed-bottom { position: fixed; right: 0; bottom: 0; left: 0; z-index: 1030; } .sticky-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } @media (min-width: 576px) { .sticky-sm-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 768px) { .sticky-md-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 992px) { .sticky-lg-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 1200px) { .sticky-xl-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 1400px) { .sticky-xxl-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } .hstack { display: flex; flex-direction: row; align-items: center; align-self: stretch; } .vstack { display: flex; flex: 1 1 auto; flex-direction: column; align-self: stretch; } .visually-hidden, .visually-hidden-focusable:not(:focus):not(:focus-within) { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; border: 0 !important; } .stretched-link::after { position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 1; content: ""; } .text-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .vr { display: inline-block; align-self: stretch; width: 1px; min-height: 1em; background-color: currentColor; opacity: 0.25; } .align-baseline { vertical-align: baseline !important; } .align-top { vertical-align: top !important; } .align-middle { vertical-align: middle !important; } .align-bottom { vertical-align: bottom !important; } .align-text-bottom { vertical-align: text-bottom !important; } .align-text-top { vertical-align: text-top !important; } .float-start { float: left !important; } .float-end { float: right !important; } .float-none { float: none !important; } .opacity-0 { opacity: 0 !important; } .opacity-25 { opacity: 0.25 !important; } .opacity-50 { opacity: 0.5 !important; } .opacity-75 { opacity: 0.75 !important; } .opacity-100 { opacity: 1 !important; } .overflow-auto { overflow: auto !important; } .overflow-hidden { overflow: hidden !important; } .overflow-visible { overflow: visible !important; } .overflow-scroll { overflow: scroll !important; } .d-inline { display: inline !important; } .d-inline-block { display: inline-block !important; } .d-block { display: block !important; } .d-grid { display: grid !important; } .d-table { display: table !important; } .d-table-row { display: table-row !important; } .d-table-cell { display: table-cell !important; } .d-flex { display: flex !important; } .d-inline-flex { display: inline-flex !important; } .d-none { display: none !important; } .shadow { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; } .shadow-sm { box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; } .shadow-lg { box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; } .shadow-none { box-shadow: none !important; } .position-static { position: static !important; } .position-relative { position: relative !important; } .position-absolute { position: absolute !important; } .position-fixed { position: fixed !important; } .position-sticky { position: -webkit-sticky !important; position: sticky !important; } .top-0 { top: 0 !important; } .top-50 { top: 50% !important; } .top-100 { top: 100% !important; } .bottom-0 { bottom: 0 !important; } .bottom-50 { bottom: 50% !important; } .bottom-100 { bottom: 100% !important; } .start-0 { left: 0 !important; } .start-50 { left: 50% !important; } .start-100 { left: 100% !important; } .end-0 { right: 0 !important; } .end-50 { right: 50% !important; } .end-100 { right: 100% !important; } .translate-middle { transform: translate(-50%, -50%) !important; } .translate-middle-x { transform: translateX(-50%) !important; } .translate-middle-y { transform: translateY(-50%) !important; } .border { border: 1px solid #dee2e6 !important; } .border-0 { border: 0 !important; } .border-top { border-top: 1px solid #dee2e6 !important; } .border-top-0 { border-top: 0 !important; } .border-end { border-right: 1px solid #dee2e6 !important; } .border-end-0 { border-right: 0 !important; } .border-bottom { border-bottom: 1px solid #dee2e6 !important; } .border-bottom-0 { border-bottom: 0 !important; } .border-start { border-left: 1px solid #dee2e6 !important; } .border-start-0 { border-left: 0 !important; } .border-primary { border-color: #0d6efd !important; } .border-secondary { border-color: #6c757d !important; } .border-success { border-color: #198754 !important; } .border-info { border-color: #0dcaf0 !important; } .border-warning { border-color: #ffc107 !important; } .border-danger { border-color: #dc3545 !important; } .border-light { border-color: #f8f9fa !important; } .border-dark { border-color: #212529 !important; } .border-white { border-color: #fff !important; } .border-1 { border-width: 1px !important; } .border-2 { border-width: 2px !important; } .border-3 { border-width: 3px !important; } .border-4 { border-width: 4px !important; } .border-5 { border-width: 5px !important; } .w-25 { width: 25% !important; } .w-50 { width: 50% !important; } .w-75 { width: 75% !important; } .w-100 { width: 100% !important; } .w-auto { width: auto !important; } .mw-100 { max-width: 100% !important; } .vw-100 { width: 100vw !important; } .min-vw-100 { min-width: 100vw !important; } .h-25 { height: 25% !important; } .h-50 { height: 50% !important; } .h-75 { height: 75% !important; } .h-100 { height: 100% !important; } .h-auto { height: auto !important; } .mh-100 { max-height: 100% !important; } .vh-100 { height: 100vh !important; } .min-vh-100 { min-height: 100vh !important; } .flex-fill { flex: 1 1 auto !important; } .flex-row { flex-direction: row !important; } .flex-column { flex-direction: column !important; } .flex-row-reverse { flex-direction: row-reverse !important; } .flex-column-reverse { flex-direction: column-reverse !important; } .flex-grow-0 { flex-grow: 0 !important; } .flex-grow-1 { flex-grow: 1 !important; } .flex-shrink-0 { flex-shrink: 0 !important; } .flex-shrink-1 { flex-shrink: 1 !important; } .flex-wrap { flex-wrap: wrap !important; } .flex-nowrap { flex-wrap: nowrap !important; } .flex-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-0 { gap: 0 !important; } .gap-1 { gap: 0.25rem !important; } .gap-2 { gap: 0.5rem !important; } .gap-3 { gap: 1rem !important; } .gap-4 { gap: 1.5rem !important; } .gap-5 { gap: 3rem !important; } .justify-content-start { justify-content: flex-start !important; } .justify-content-end { justify-content: flex-end !important; } .justify-content-center { justify-content: center !important; } .justify-content-between { justify-content: space-between !important; } .justify-content-around { justify-content: space-around !important; } .justify-content-evenly { justify-content: space-evenly !important; } .align-items-start { align-items: flex-start !important; } .align-items-end { align-items: flex-end !important; } .align-items-center { align-items: center !important; } .align-items-baseline { align-items: baseline !important; } .align-items-stretch { align-items: stretch !important; } .align-content-start { align-content: flex-start !important; } .align-content-end { align-content: flex-end !important; } .align-content-center { align-content: center !important; } .align-content-between { align-content: space-between !important; } .align-content-around { align-content: space-around !important; } .align-content-stretch { align-content: stretch !important; } .align-self-auto { align-self: auto !important; } .align-self-start { align-self: flex-start !important; } .align-self-end { align-self: flex-end !important; } .align-self-center { align-self: center !important; } .align-self-baseline { align-self: baseline !important; } .align-self-stretch { align-self: stretch !important; } .order-first { order: -1 !important; } .order-0 { order: 0 !important; } .order-1 { order: 1 !important; } .order-2 { order: 2 !important; } .order-3 { order: 3 !important; } .order-4 { order: 4 !important; } .order-5 { order: 5 !important; } .order-last { order: 6 !important; } .m-0 { margin: 0 !important; } .m-1 { margin: 0.25rem !important; } .m-2 { margin: 0.5rem !important; } .m-3 { margin: 1rem !important; } .m-4 { margin: 1.5rem !important; } .m-5 { margin: 3rem !important; } .m-auto { margin: auto !important; } .mx-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-auto { margin-right: auto !important; margin-left: auto !important; } .my-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-0 { margin-top: 0 !important; } .mt-1 { margin-top: 0.25rem !important; } .mt-2 { margin-top: 0.5rem !important; } .mt-3 { margin-top: 1rem !important; } .mt-4 { margin-top: 1.5rem !important; } .mt-5 { margin-top: 3rem !important; } .mt-auto { margin-top: auto !important; } .me-0 { margin-right: 0 !important; } .me-1 { margin-right: 0.25rem !important; } .me-2 { margin-right: 0.5rem !important; } .me-3 { margin-right: 1rem !important; } .me-4 { margin-right: 1.5rem !important; } .me-5 { margin-right: 3rem !important; } .me-auto { margin-right: auto !important; } .mb-0 { margin-bottom: 0 !important; } .mb-1 { margin-bottom: 0.25rem !important; } .mb-2 { margin-bottom: 0.5rem !important; } .mb-3 { margin-bottom: 1rem !important; } .mb-4 { margin-bottom: 1.5rem !important; } .mb-5 { margin-bottom: 3rem !important; } .mb-auto { margin-bottom: auto !important; } .ms-0 { margin-left: 0 !important; } .ms-1 { margin-left: 0.25rem !important; } .ms-2 { margin-left: 0.5rem !important; } .ms-3 { margin-left: 1rem !important; } .ms-4 { margin-left: 1.5rem !important; } .ms-5 { margin-left: 3rem !important; } .ms-auto { margin-left: auto !important; } .p-0 { padding: 0 !important; } .p-1 { padding: 0.25rem !important; } .p-2 { padding: 0.5rem !important; } .p-3 { padding: 1rem !important; } .p-4 { padding: 1.5rem !important; } .p-5 { padding: 3rem !important; } .px-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-0 { padding-top: 0 !important; } .pt-1 { padding-top: 0.25rem !important; } .pt-2 { padding-top: 0.5rem !important; } .pt-3 { padding-top: 1rem !important; } .pt-4 { padding-top: 1.5rem !important; } .pt-5 { padding-top: 3rem !important; } .pe-0 { padding-right: 0 !important; } .pe-1 { padding-right: 0.25rem !important; } .pe-2 { padding-right: 0.5rem !important; } .pe-3 { padding-right: 1rem !important; } .pe-4 { padding-right: 1.5rem !important; } .pe-5 { padding-right: 3rem !important; } .pb-0 { padding-bottom: 0 !important; } .pb-1 { padding-bottom: 0.25rem !important; } .pb-2 { padding-bottom: 0.5rem !important; } .pb-3 { padding-bottom: 1rem !important; } .pb-4 { padding-bottom: 1.5rem !important; } .pb-5 { padding-bottom: 3rem !important; } .ps-0 { padding-left: 0 !important; } .ps-1 { padding-left: 0.25rem !important; } .ps-2 { padding-left: 0.5rem !important; } .ps-3 { padding-left: 1rem !important; } .ps-4 { padding-left: 1.5rem !important; } .ps-5 { padding-left: 3rem !important; } .font-monospace { font-family: var(--bs-font-monospace) !important; } .fs-1 { font-size: calc(1.375rem + 1.5vw) !important; } .fs-2 { font-size: calc(1.325rem + 0.9vw) !important; } .fs-3 { font-size: calc(1.3rem + 0.6vw) !important; } .fs-4 { font-size: calc(1.275rem + 0.3vw) !important; } .fs-5 { font-size: 1.25rem !important; } .fs-6 { font-size: 1rem !important; } .fst-italic { font-style: italic !important; } .fst-normal { font-style: normal !important; } .fw-light { font-weight: 300 !important; } .fw-lighter { font-weight: lighter !important; } .fw-normal { font-weight: 400 !important; } .fw-bold { font-weight: 700 !important; } .fw-bolder { font-weight: bolder !important; } .lh-1 { line-height: 1 !important; } .lh-sm { line-height: 1.25 !important; } .lh-base { line-height: 1.5 !important; } .lh-lg { line-height: 2 !important; } .text-start { text-align: left !important; } .text-end { text-align: right !important; } .text-center { text-align: center !important; } .text-decoration-none { text-decoration: none !important; } .text-decoration-underline { text-decoration: underline !important; } .text-decoration-line-through { text-decoration: line-through !important; } .text-lowercase { text-transform: lowercase !important; } .text-uppercase { text-transform: uppercase !important; } .text-capitalize { text-transform: capitalize !important; } .text-wrap { white-space: normal !important; } .text-nowrap { white-space: nowrap !important; } /* rtl:begin:remove */ .text-break { word-wrap: break-word !important; word-break: break-word !important; } /* rtl:end:remove */ .text-primary { --bs-text-opacity: 1; color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; } .text-secondary { --bs-text-opacity: 1; color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; } .text-success { --bs-text-opacity: 1; color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; } .text-info { --bs-text-opacity: 1; color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; } .text-warning { --bs-text-opacity: 1; color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; } .text-danger { --bs-text-opacity: 1; color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; } .text-light { --bs-text-opacity: 1; color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; } .text-dark { --bs-text-opacity: 1; color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; } .text-black { --bs-text-opacity: 1; color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; } .text-white { --bs-text-opacity: 1; color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; } .text-body { --bs-text-opacity: 1; color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; } .text-muted { --bs-text-opacity: 1; color: #6c757d !important; } .text-black-50 { --bs-text-opacity: 1; color: rgba(0, 0, 0, 0.5) !important; } .text-white-50 { --bs-text-opacity: 1; color: rgba(255, 255, 255, 0.5) !important; } .text-reset { --bs-text-opacity: 1; color: inherit !important; } .text-opacity-25 { --bs-text-opacity: 0.25; } .text-opacity-50 { --bs-text-opacity: 0.5; } .text-opacity-75 { --bs-text-opacity: 0.75; } .text-opacity-100 { --bs-text-opacity: 1; } .bg-primary { --bs-bg-opacity: 1; background-color: rgba( var(--bs-primary-rgb), var(--bs-bg-opacity) ) !important; } .bg-secondary { --bs-bg-opacity: 1; background-color: rgba( var(--bs-secondary-rgb), var(--bs-bg-opacity) ) !important; } .bg-success { --bs-bg-opacity: 1; background-color: rgba( var(--bs-success-rgb), var(--bs-bg-opacity) ) !important; } .bg-info { --bs-bg-opacity: 1; background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; } .bg-warning { --bs-bg-opacity: 1; background-color: rgba( var(--bs-warning-rgb), var(--bs-bg-opacity) ) !important; } .bg-danger { --bs-bg-opacity: 1; background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; } .bg-light { --bs-bg-opacity: 1; background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; } .bg-dark { --bs-bg-opacity: 1; background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } .bg-black { --bs-bg-opacity: 1; background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; } .bg-white { --bs-bg-opacity: 1; background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; } .bg-body { --bs-bg-opacity: 1; background-color: rgba( var(--bs-body-bg-rgb), var(--bs-bg-opacity) ) !important; } .bg-transparent { --bs-bg-opacity: 1; background-color: transparent !important; } .bg-opacity-10 { --bs-bg-opacity: 0.1; } .bg-opacity-25 { --bs-bg-opacity: 0.25; } .bg-opacity-50 { --bs-bg-opacity: 0.5; } .bg-opacity-75 { --bs-bg-opacity: 0.75; } .bg-opacity-100 { --bs-bg-opacity: 1; } .bg-gradient { background-image: var(--bs-gradient) !important; } .user-select-all { -webkit-user-select: all !important; -moz-user-select: all !important; user-select: all !important; } .user-select-auto { -webkit-user-select: auto !important; -moz-user-select: auto !important; user-select: auto !important; } .user-select-none { -webkit-user-select: none !important; -moz-user-select: none !important; user-select: none !important; } .pe-none { pointer-events: none !important; } .pe-auto { pointer-events: auto !important; } .rounded { border-radius: 0.25rem !important; } .rounded-0 { border-radius: 0 !important; } .rounded-1 { border-radius: 0.2rem !important; } .rounded-2 { border-radius: 0.25rem !important; } .rounded-3 { border-radius: 0.3rem !important; } .rounded-circle { border-radius: 50% !important; } .rounded-pill { border-radius: 50rem !important; } .rounded-top { border-top-left-radius: 0.25rem !important; border-top-right-radius: 0.25rem !important; } .rounded-end { border-top-right-radius: 0.25rem !important; border-bottom-right-radius: 0.25rem !important; } .rounded-bottom { border-bottom-right-radius: 0.25rem !important; border-bottom-left-radius: 0.25rem !important; } .rounded-start { border-bottom-left-radius: 0.25rem !important; border-top-left-radius: 0.25rem !important; } .visible { visibility: visible !important; } .invisible { visibility: hidden !important; } @media (min-width: 576px) { .float-sm-start { float: left !important; } .float-sm-end { float: right !important; } .float-sm-none { float: none !important; } .d-sm-inline { display: inline !important; } .d-sm-inline-block { display: inline-block !important; } .d-sm-block { display: block !important; } .d-sm-grid { display: grid !important; } .d-sm-table { display: table !important; } .d-sm-table-row { display: table-row !important; } .d-sm-table-cell { display: table-cell !important; } .d-sm-flex { display: flex !important; } .d-sm-inline-flex { display: inline-flex !important; } .d-sm-none { display: none !important; } .flex-sm-fill { flex: 1 1 auto !important; } .flex-sm-row { flex-direction: row !important; } .flex-sm-column { flex-direction: column !important; } .flex-sm-row-reverse { flex-direction: row-reverse !important; } .flex-sm-column-reverse { flex-direction: column-reverse !important; } .flex-sm-grow-0 { flex-grow: 0 !important; } .flex-sm-grow-1 { flex-grow: 1 !important; } .flex-sm-shrink-0 { flex-shrink: 0 !important; } .flex-sm-shrink-1 { flex-shrink: 1 !important; } .flex-sm-wrap { flex-wrap: wrap !important; } .flex-sm-nowrap { flex-wrap: nowrap !important; } .flex-sm-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-sm-0 { gap: 0 !important; } .gap-sm-1 { gap: 0.25rem !important; } .gap-sm-2 { gap: 0.5rem !important; } .gap-sm-3 { gap: 1rem !important; } .gap-sm-4 { gap: 1.5rem !important; } .gap-sm-5 { gap: 3rem !important; } .justify-content-sm-start { justify-content: flex-start !important; } .justify-content-sm-end { justify-content: flex-end !important; } .justify-content-sm-center { justify-content: center !important; } .justify-content-sm-between { justify-content: space-between !important; } .justify-content-sm-around { justify-content: space-around !important; } .justify-content-sm-evenly { justify-content: space-evenly !important; } .align-items-sm-start { align-items: flex-start !important; } .align-items-sm-end { align-items: flex-end !important; } .align-items-sm-center { align-items: center !important; } .align-items-sm-baseline { align-items: baseline !important; } .align-items-sm-stretch { align-items: stretch !important; } .align-content-sm-start { align-content: flex-start !important; } .align-content-sm-end { align-content: flex-end !important; } .align-content-sm-center { align-content: center !important; } .align-content-sm-between { align-content: space-between !important; } .align-content-sm-around { align-content: space-around !important; } .align-content-sm-stretch { align-content: stretch !important; } .align-self-sm-auto { align-self: auto !important; } .align-self-sm-start { align-self: flex-start !important; } .align-self-sm-end { align-self: flex-end !important; } .align-self-sm-center { align-self: center !important; } .align-self-sm-baseline { align-self: baseline !important; } .align-self-sm-stretch { align-self: stretch !important; } .order-sm-first { order: -1 !important; } .order-sm-0 { order: 0 !important; } .order-sm-1 { order: 1 !important; } .order-sm-2 { order: 2 !important; } .order-sm-3 { order: 3 !important; } .order-sm-4 { order: 4 !important; } .order-sm-5 { order: 5 !important; } .order-sm-last { order: 6 !important; } .m-sm-0 { margin: 0 !important; } .m-sm-1 { margin: 0.25rem !important; } .m-sm-2 { margin: 0.5rem !important; } .m-sm-3 { margin: 1rem !important; } .m-sm-4 { margin: 1.5rem !important; } .m-sm-5 { margin: 3rem !important; } .m-sm-auto { margin: auto !important; } .mx-sm-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-sm-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-sm-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-sm-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-sm-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-sm-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-sm-auto { margin-right: auto !important; margin-left: auto !important; } .my-sm-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-sm-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-sm-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-sm-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-sm-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-sm-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-sm-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-sm-0 { margin-top: 0 !important; } .mt-sm-1 { margin-top: 0.25rem !important; } .mt-sm-2 { margin-top: 0.5rem !important; } .mt-sm-3 { margin-top: 1rem !important; } .mt-sm-4 { margin-top: 1.5rem !important; } .mt-sm-5 { margin-top: 3rem !important; } .mt-sm-auto { margin-top: auto !important; } .me-sm-0 { margin-right: 0 !important; } .me-sm-1 { margin-right: 0.25rem !important; } .me-sm-2 { margin-right: 0.5rem !important; } .me-sm-3 { margin-right: 1rem !important; } .me-sm-4 { margin-right: 1.5rem !important; } .me-sm-5 { margin-right: 3rem !important; } .me-sm-auto { margin-right: auto !important; } .mb-sm-0 { margin-bottom: 0 !important; } .mb-sm-1 { margin-bottom: 0.25rem !important; } .mb-sm-2 { margin-bottom: 0.5rem !important; } .mb-sm-3 { margin-bottom: 1rem !important; } .mb-sm-4 { margin-bottom: 1.5rem !important; } .mb-sm-5 { margin-bottom: 3rem !important; } .mb-sm-auto { margin-bottom: auto !important; } .ms-sm-0 { margin-left: 0 !important; } .ms-sm-1 { margin-left: 0.25rem !important; } .ms-sm-2 { margin-left: 0.5rem !important; } .ms-sm-3 { margin-left: 1rem !important; } .ms-sm-4 { margin-left: 1.5rem !important; } .ms-sm-5 { margin-left: 3rem !important; } .ms-sm-auto { margin-left: auto !important; } .p-sm-0 { padding: 0 !important; } .p-sm-1 { padding: 0.25rem !important; } .p-sm-2 { padding: 0.5rem !important; } .p-sm-3 { padding: 1rem !important; } .p-sm-4 { padding: 1.5rem !important; } .p-sm-5 { padding: 3rem !important; } .px-sm-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-sm-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-sm-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-sm-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-sm-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-sm-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-sm-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-sm-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-sm-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-sm-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-sm-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-sm-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-sm-0 { padding-top: 0 !important; } .pt-sm-1 { padding-top: 0.25rem !important; } .pt-sm-2 { padding-top: 0.5rem !important; } .pt-sm-3 { padding-top: 1rem !important; } .pt-sm-4 { padding-top: 1.5rem !important; } .pt-sm-5 { padding-top: 3rem !important; } .pe-sm-0 { padding-right: 0 !important; } .pe-sm-1 { padding-right: 0.25rem !important; } .pe-sm-2 { padding-right: 0.5rem !important; } .pe-sm-3 { padding-right: 1rem !important; } .pe-sm-4 { padding-right: 1.5rem !important; } .pe-sm-5 { padding-right: 3rem !important; } .pb-sm-0 { padding-bottom: 0 !important; } .pb-sm-1 { padding-bottom: 0.25rem !important; } .pb-sm-2 { padding-bottom: 0.5rem !important; } .pb-sm-3 { padding-bottom: 1rem !important; } .pb-sm-4 { padding-bottom: 1.5rem !important; } .pb-sm-5 { padding-bottom: 3rem !important; } .ps-sm-0 { padding-left: 0 !important; } .ps-sm-1 { padding-left: 0.25rem !important; } .ps-sm-2 { padding-left: 0.5rem !important; } .ps-sm-3 { padding-left: 1rem !important; } .ps-sm-4 { padding-left: 1.5rem !important; } .ps-sm-5 { padding-left: 3rem !important; } .text-sm-start { text-align: left !important; } .text-sm-end { text-align: right !important; } .text-sm-center { text-align: center !important; } } @media (min-width: 768px) { .float-md-start { float: left !important; } .float-md-end { float: right !important; } .float-md-none { float: none !important; } .d-md-inline { display: inline !important; } .d-md-inline-block { display: inline-block !important; } .d-md-block { display: block !important; } .d-md-grid { display: grid !important; } .d-md-table { display: table !important; } .d-md-table-row { display: table-row !important; } .d-md-table-cell { display: table-cell !important; } .d-md-flex { display: flex !important; } .d-md-inline-flex { display: inline-flex !important; } .d-md-none { display: none !important; } .flex-md-fill { flex: 1 1 auto !important; } .flex-md-row { flex-direction: row !important; } .flex-md-column { flex-direction: column !important; } .flex-md-row-reverse { flex-direction: row-reverse !important; } .flex-md-column-reverse { flex-direction: column-reverse !important; } .flex-md-grow-0 { flex-grow: 0 !important; } .flex-md-grow-1 { flex-grow: 1 !important; } .flex-md-shrink-0 { flex-shrink: 0 !important; } .flex-md-shrink-1 { flex-shrink: 1 !important; } .flex-md-wrap { flex-wrap: wrap !important; } .flex-md-nowrap { flex-wrap: nowrap !important; } .flex-md-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-md-0 { gap: 0 !important; } .gap-md-1 { gap: 0.25rem !important; } .gap-md-2 { gap: 0.5rem !important; } .gap-md-3 { gap: 1rem !important; } .gap-md-4 { gap: 1.5rem !important; } .gap-md-5 { gap: 3rem !important; } .justify-content-md-start { justify-content: flex-start !important; } .justify-content-md-end { justify-content: flex-end !important; } .justify-content-md-center { justify-content: center !important; } .justify-content-md-between { justify-content: space-between !important; } .justify-content-md-around { justify-content: space-around !important; } .justify-content-md-evenly { justify-content: space-evenly !important; } .align-items-md-start { align-items: flex-start !important; } .align-items-md-end { align-items: flex-end !important; } .align-items-md-center { align-items: center !important; } .align-items-md-baseline { align-items: baseline !important; } .align-items-md-stretch { align-items: stretch !important; } .align-content-md-start { align-content: flex-start !important; } .align-content-md-end { align-content: flex-end !important; } .align-content-md-center { align-content: center !important; } .align-content-md-between { align-content: space-between !important; } .align-content-md-around { align-content: space-around !important; } .align-content-md-stretch { align-content: stretch !important; } .align-self-md-auto { align-self: auto !important; } .align-self-md-start { align-self: flex-start !important; } .align-self-md-end { align-self: flex-end !important; } .align-self-md-center { align-self: center !important; } .align-self-md-baseline { align-self: baseline !important; } .align-self-md-stretch { align-self: stretch !important; } .order-md-first { order: -1 !important; } .order-md-0 { order: 0 !important; } .order-md-1 { order: 1 !important; } .order-md-2 { order: 2 !important; } .order-md-3 { order: 3 !important; } .order-md-4 { order: 4 !important; } .order-md-5 { order: 5 !important; } .order-md-last { order: 6 !important; } .m-md-0 { margin: 0 !important; } .m-md-1 { margin: 0.25rem !important; } .m-md-2 { margin: 0.5rem !important; } .m-md-3 { margin: 1rem !important; } .m-md-4 { margin: 1.5rem !important; } .m-md-5 { margin: 3rem !important; } .m-md-auto { margin: auto !important; } .mx-md-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-md-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-md-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-md-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-md-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-md-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-md-auto { margin-right: auto !important; margin-left: auto !important; } .my-md-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-md-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-md-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-md-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-md-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-md-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-md-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-md-0 { margin-top: 0 !important; } .mt-md-1 { margin-top: 0.25rem !important; } .mt-md-2 { margin-top: 0.5rem !important; } .mt-md-3 { margin-top: 1rem !important; } .mt-md-4 { margin-top: 1.5rem !important; } .mt-md-5 { margin-top: 3rem !important; } .mt-md-auto { margin-top: auto !important; } .me-md-0 { margin-right: 0 !important; } .me-md-1 { margin-right: 0.25rem !important; } .me-md-2 { margin-right: 0.5rem !important; } .me-md-3 { margin-right: 1rem !important; } .me-md-4 { margin-right: 1.5rem !important; } .me-md-5 { margin-right: 3rem !important; } .me-md-auto { margin-right: auto !important; } .mb-md-0 { margin-bottom: 0 !important; } .mb-md-1 { margin-bottom: 0.25rem !important; } .mb-md-2 { margin-bottom: 0.5rem !important; } .mb-md-3 { margin-bottom: 1rem !important; } .mb-md-4 { margin-bottom: 1.5rem !important; } .mb-md-5 { margin-bottom: 3rem !important; } .mb-md-auto { margin-bottom: auto !important; } .ms-md-0 { margin-left: 0 !important; } .ms-md-1 { margin-left: 0.25rem !important; } .ms-md-2 { margin-left: 0.5rem !important; } .ms-md-3 { margin-left: 1rem !important; } .ms-md-4 { margin-left: 1.5rem !important; } .ms-md-5 { margin-left: 3rem !important; } .ms-md-auto { margin-left: auto !important; } .p-md-0 { padding: 0 !important; } .p-md-1 { padding: 0.25rem !important; } .p-md-2 { padding: 0.5rem !important; } .p-md-3 { padding: 1rem !important; } .p-md-4 { padding: 1.5rem !important; } .p-md-5 { padding: 3rem !important; } .px-md-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-md-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-md-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-md-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-md-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-md-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-md-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-md-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-md-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-md-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-md-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-md-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-md-0 { padding-top: 0 !important; } .pt-md-1 { padding-top: 0.25rem !important; } .pt-md-2 { padding-top: 0.5rem !important; } .pt-md-3 { padding-top: 1rem !important; } .pt-md-4 { padding-top: 1.5rem !important; } .pt-md-5 { padding-top: 3rem !important; } .pe-md-0 { padding-right: 0 !important; } .pe-md-1 { padding-right: 0.25rem !important; } .pe-md-2 { padding-right: 0.5rem !important; } .pe-md-3 { padding-right: 1rem !important; } .pe-md-4 { padding-right: 1.5rem !important; } .pe-md-5 { padding-right: 3rem !important; } .pb-md-0 { padding-bottom: 0 !important; } .pb-md-1 { padding-bottom: 0.25rem !important; } .pb-md-2 { padding-bottom: 0.5rem !important; } .pb-md-3 { padding-bottom: 1rem !important; } .pb-md-4 { padding-bottom: 1.5rem !important; } .pb-md-5 { padding-bottom: 3rem !important; } .ps-md-0 { padding-left: 0 !important; } .ps-md-1 { padding-left: 0.25rem !important; } .ps-md-2 { padding-left: 0.5rem !important; } .ps-md-3 { padding-left: 1rem !important; } .ps-md-4 { padding-left: 1.5rem !important; } .ps-md-5 { padding-left: 3rem !important; } .text-md-start { text-align: left !important; } .text-md-end { text-align: right !important; } .text-md-center { text-align: center !important; } } @media (min-width: 992px) { .float-lg-start { float: left !important; } .float-lg-end { float: right !important; } .float-lg-none { float: none !important; } .d-lg-inline { display: inline !important; } .d-lg-inline-block { display: inline-block !important; } .d-lg-block { display: block !important; } .d-lg-grid { display: grid !important; } .d-lg-table { display: table !important; } .d-lg-table-row { display: table-row !important; } .d-lg-table-cell { display: table-cell !important; } .d-lg-flex { display: flex !important; } .d-lg-inline-flex { display: inline-flex !important; } .d-lg-none { display: none !important; } .flex-lg-fill { flex: 1 1 auto !important; } .flex-lg-row { flex-direction: row !important; } .flex-lg-column { flex-direction: column !important; } .flex-lg-row-reverse { flex-direction: row-reverse !important; } .flex-lg-column-reverse { flex-direction: column-reverse !important; } .flex-lg-grow-0 { flex-grow: 0 !important; } .flex-lg-grow-1 { flex-grow: 1 !important; } .flex-lg-shrink-0 { flex-shrink: 0 !important; } .flex-lg-shrink-1 { flex-shrink: 1 !important; } .flex-lg-wrap { flex-wrap: wrap !important; } .flex-lg-nowrap { flex-wrap: nowrap !important; } .flex-lg-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-lg-0 { gap: 0 !important; } .gap-lg-1 { gap: 0.25rem !important; } .gap-lg-2 { gap: 0.5rem !important; } .gap-lg-3 { gap: 1rem !important; } .gap-lg-4 { gap: 1.5rem !important; } .gap-lg-5 { gap: 3rem !important; } .justify-content-lg-start { justify-content: flex-start !important; } .justify-content-lg-end { justify-content: flex-end !important; } .justify-content-lg-center { justify-content: center !important; } .justify-content-lg-between { justify-content: space-between !important; } .justify-content-lg-around { justify-content: space-around !important; } .justify-content-lg-evenly { justify-content: space-evenly !important; } .align-items-lg-start { align-items: flex-start !important; } .align-items-lg-end { align-items: flex-end !important; } .align-items-lg-center { align-items: center !important; } .align-items-lg-baseline { align-items: baseline !important; } .align-items-lg-stretch { align-items: stretch !important; } .align-content-lg-start { align-content: flex-start !important; } .align-content-lg-end { align-content: flex-end !important; } .align-content-lg-center { align-content: center !important; } .align-content-lg-between { align-content: space-between !important; } .align-content-lg-around { align-content: space-around !important; } .align-content-lg-stretch { align-content: stretch !important; } .align-self-lg-auto { align-self: auto !important; } .align-self-lg-start { align-self: flex-start !important; } .align-self-lg-end { align-self: flex-end !important; } .align-self-lg-center { align-self: center !important; } .align-self-lg-baseline { align-self: baseline !important; } .align-self-lg-stretch { align-self: stretch !important; } .order-lg-first { order: -1 !important; } .order-lg-0 { order: 0 !important; } .order-lg-1 { order: 1 !important; } .order-lg-2 { order: 2 !important; } .order-lg-3 { order: 3 !important; } .order-lg-4 { order: 4 !important; } .order-lg-5 { order: 5 !important; } .order-lg-last { order: 6 !important; } .m-lg-0 { margin: 0 !important; } .m-lg-1 { margin: 0.25rem !important; } .m-lg-2 { margin: 0.5rem !important; } .m-lg-3 { margin: 1rem !important; } .m-lg-4 { margin: 1.5rem !important; } .m-lg-5 { margin: 3rem !important; } .m-lg-auto { margin: auto !important; } .mx-lg-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-lg-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-lg-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-lg-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-lg-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-lg-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-lg-auto { margin-right: auto !important; margin-left: auto !important; } .my-lg-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-lg-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-lg-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-lg-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-lg-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-lg-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-lg-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-lg-0 { margin-top: 0 !important; } .mt-lg-1 { margin-top: 0.25rem !important; } .mt-lg-2 { margin-top: 0.5rem !important; } .mt-lg-3 { margin-top: 1rem !important; } .mt-lg-4 { margin-top: 1.5rem !important; } .mt-lg-5 { margin-top: 3rem !important; } .mt-lg-auto { margin-top: auto !important; } .me-lg-0 { margin-right: 0 !important; } .me-lg-1 { margin-right: 0.25rem !important; } .me-lg-2 { margin-right: 0.5rem !important; } .me-lg-3 { margin-right: 1rem !important; } .me-lg-4 { margin-right: 1.5rem !important; } .me-lg-5 { margin-right: 3rem !important; } .me-lg-auto { margin-right: auto !important; } .mb-lg-0 { margin-bottom: 0 !important; } .mb-lg-1 { margin-bottom: 0.25rem !important; } .mb-lg-2 { margin-bottom: 0.5rem !important; } .mb-lg-3 { margin-bottom: 1rem !important; } .mb-lg-4 { margin-bottom: 1.5rem !important; } .mb-lg-5 { margin-bottom: 3rem !important; } .mb-lg-auto { margin-bottom: auto !important; } .ms-lg-0 { margin-left: 0 !important; } .ms-lg-1 { margin-left: 0.25rem !important; } .ms-lg-2 { margin-left: 0.5rem !important; } .ms-lg-3 { margin-left: 1rem !important; } .ms-lg-4 { margin-left: 1.5rem !important; } .ms-lg-5 { margin-left: 3rem !important; } .ms-lg-auto { margin-left: auto !important; } .p-lg-0 { padding: 0 !important; } .p-lg-1 { padding: 0.25rem !important; } .p-lg-2 { padding: 0.5rem !important; } .p-lg-3 { padding: 1rem !important; } .p-lg-4 { padding: 1.5rem !important; } .p-lg-5 { padding: 3rem !important; } .px-lg-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-lg-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-lg-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-lg-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-lg-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-lg-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-lg-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-lg-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-lg-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-lg-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-lg-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-lg-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-lg-0 { padding-top: 0 !important; } .pt-lg-1 { padding-top: 0.25rem !important; } .pt-lg-2 { padding-top: 0.5rem !important; } .pt-lg-3 { padding-top: 1rem !important; } .pt-lg-4 { padding-top: 1.5rem !important; } .pt-lg-5 { padding-top: 3rem !important; } .pe-lg-0 { padding-right: 0 !important; } .pe-lg-1 { padding-right: 0.25rem !important; } .pe-lg-2 { padding-right: 0.5rem !important; } .pe-lg-3 { padding-right: 1rem !important; } .pe-lg-4 { padding-right: 1.5rem !important; } .pe-lg-5 { padding-right: 3rem !important; } .pb-lg-0 { padding-bottom: 0 !important; } .pb-lg-1 { padding-bottom: 0.25rem !important; } .pb-lg-2 { padding-bottom: 0.5rem !important; } .pb-lg-3 { padding-bottom: 1rem !important; } .pb-lg-4 { padding-bottom: 1.5rem !important; } .pb-lg-5 { padding-bottom: 3rem !important; } .ps-lg-0 { padding-left: 0 !important; } .ps-lg-1 { padding-left: 0.25rem !important; } .ps-lg-2 { padding-left: 0.5rem !important; } .ps-lg-3 { padding-left: 1rem !important; } .ps-lg-4 { padding-left: 1.5rem !important; } .ps-lg-5 { padding-left: 3rem !important; } .text-lg-start { text-align: left !important; } .text-lg-end { text-align: right !important; } .text-lg-center { text-align: center !important; } } @media (min-width: 1200px) { .float-xl-start { float: left !important; } .float-xl-end { float: right !important; } .float-xl-none { float: none !important; } .d-xl-inline { display: inline !important; } .d-xl-inline-block { display: inline-block !important; } .d-xl-block { display: block !important; } .d-xl-grid { display: grid !important; } .d-xl-table { display: table !important; } .d-xl-table-row { display: table-row !important; } .d-xl-table-cell { display: table-cell !important; } .d-xl-flex { display: flex !important; } .d-xl-inline-flex { display: inline-flex !important; } .d-xl-none { display: none !important; } .flex-xl-fill { flex: 1 1 auto !important; } .flex-xl-row { flex-direction: row !important; } .flex-xl-column { flex-direction: column !important; } .flex-xl-row-reverse { flex-direction: row-reverse !important; } .flex-xl-column-reverse { flex-direction: column-reverse !important; } .flex-xl-grow-0 { flex-grow: 0 !important; } .flex-xl-grow-1 { flex-grow: 1 !important; } .flex-xl-shrink-0 { flex-shrink: 0 !important; } .flex-xl-shrink-1 { flex-shrink: 1 !important; } .flex-xl-wrap { flex-wrap: wrap !important; } .flex-xl-nowrap { flex-wrap: nowrap !important; } .flex-xl-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-xl-0 { gap: 0 !important; } .gap-xl-1 { gap: 0.25rem !important; } .gap-xl-2 { gap: 0.5rem !important; } .gap-xl-3 { gap: 1rem !important; } .gap-xl-4 { gap: 1.5rem !important; } .gap-xl-5 { gap: 3rem !important; } .justify-content-xl-start { justify-content: flex-start !important; } .justify-content-xl-end { justify-content: flex-end !important; } .justify-content-xl-center { justify-content: center !important; } .justify-content-xl-between { justify-content: space-between !important; } .justify-content-xl-around { justify-content: space-around !important; } .justify-content-xl-evenly { justify-content: space-evenly !important; } .align-items-xl-start { align-items: flex-start !important; } .align-items-xl-end { align-items: flex-end !important; } .align-items-xl-center { align-items: center !important; } .align-items-xl-baseline { align-items: baseline !important; } .align-items-xl-stretch { align-items: stretch !important; } .align-content-xl-start { align-content: flex-start !important; } .align-content-xl-end { align-content: flex-end !important; } .align-content-xl-center { align-content: center !important; } .align-content-xl-between { align-content: space-between !important; } .align-content-xl-around { align-content: space-around !important; } .align-content-xl-stretch { align-content: stretch !important; } .align-self-xl-auto { align-self: auto !important; } .align-self-xl-start { align-self: flex-start !important; } .align-self-xl-end { align-self: flex-end !important; } .align-self-xl-center { align-self: center !important; } .align-self-xl-baseline { align-self: baseline !important; } .align-self-xl-stretch { align-self: stretch !important; } .order-xl-first { order: -1 !important; } .order-xl-0 { order: 0 !important; } .order-xl-1 { order: 1 !important; } .order-xl-2 { order: 2 !important; } .order-xl-3 { order: 3 !important; } .order-xl-4 { order: 4 !important; } .order-xl-5 { order: 5 !important; } .order-xl-last { order: 6 !important; } .m-xl-0 { margin: 0 !important; } .m-xl-1 { margin: 0.25rem !important; } .m-xl-2 { margin: 0.5rem !important; } .m-xl-3 { margin: 1rem !important; } .m-xl-4 { margin: 1.5rem !important; } .m-xl-5 { margin: 3rem !important; } .m-xl-auto { margin: auto !important; } .mx-xl-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-xl-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-xl-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-xl-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-xl-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-xl-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-xl-auto { margin-right: auto !important; margin-left: auto !important; } .my-xl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xl-0 { margin-top: 0 !important; } .mt-xl-1 { margin-top: 0.25rem !important; } .mt-xl-2 { margin-top: 0.5rem !important; } .mt-xl-3 { margin-top: 1rem !important; } .mt-xl-4 { margin-top: 1.5rem !important; } .mt-xl-5 { margin-top: 3rem !important; } .mt-xl-auto { margin-top: auto !important; } .me-xl-0 { margin-right: 0 !important; } .me-xl-1 { margin-right: 0.25rem !important; } .me-xl-2 { margin-right: 0.5rem !important; } .me-xl-3 { margin-right: 1rem !important; } .me-xl-4 { margin-right: 1.5rem !important; } .me-xl-5 { margin-right: 3rem !important; } .me-xl-auto { margin-right: auto !important; } .mb-xl-0 { margin-bottom: 0 !important; } .mb-xl-1 { margin-bottom: 0.25rem !important; } .mb-xl-2 { margin-bottom: 0.5rem !important; } .mb-xl-3 { margin-bottom: 1rem !important; } .mb-xl-4 { margin-bottom: 1.5rem !important; } .mb-xl-5 { margin-bottom: 3rem !important; } .mb-xl-auto { margin-bottom: auto !important; } .ms-xl-0 { margin-left: 0 !important; } .ms-xl-1 { margin-left: 0.25rem !important; } .ms-xl-2 { margin-left: 0.5rem !important; } .ms-xl-3 { margin-left: 1rem !important; } .ms-xl-4 { margin-left: 1.5rem !important; } .ms-xl-5 { margin-left: 3rem !important; } .ms-xl-auto { margin-left: auto !important; } .p-xl-0 { padding: 0 !important; } .p-xl-1 { padding: 0.25rem !important; } .p-xl-2 { padding: 0.5rem !important; } .p-xl-3 { padding: 1rem !important; } .p-xl-4 { padding: 1.5rem !important; } .p-xl-5 { padding: 3rem !important; } .px-xl-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-xl-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-xl-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-xl-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-xl-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-xl-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-xl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xl-0 { padding-top: 0 !important; } .pt-xl-1 { padding-top: 0.25rem !important; } .pt-xl-2 { padding-top: 0.5rem !important; } .pt-xl-3 { padding-top: 1rem !important; } .pt-xl-4 { padding-top: 1.5rem !important; } .pt-xl-5 { padding-top: 3rem !important; } .pe-xl-0 { padding-right: 0 !important; } .pe-xl-1 { padding-right: 0.25rem !important; } .pe-xl-2 { padding-right: 0.5rem !important; } .pe-xl-3 { padding-right: 1rem !important; } .pe-xl-4 { padding-right: 1.5rem !important; } .pe-xl-5 { padding-right: 3rem !important; } .pb-xl-0 { padding-bottom: 0 !important; } .pb-xl-1 { padding-bottom: 0.25rem !important; } .pb-xl-2 { padding-bottom: 0.5rem !important; } .pb-xl-3 { padding-bottom: 1rem !important; } .pb-xl-4 { padding-bottom: 1.5rem !important; } .pb-xl-5 { padding-bottom: 3rem !important; } .ps-xl-0 { padding-left: 0 !important; } .ps-xl-1 { padding-left: 0.25rem !important; } .ps-xl-2 { padding-left: 0.5rem !important; } .ps-xl-3 { padding-left: 1rem !important; } .ps-xl-4 { padding-left: 1.5rem !important; } .ps-xl-5 { padding-left: 3rem !important; } .text-xl-start { text-align: left !important; } .text-xl-end { text-align: right !important; } .text-xl-center { text-align: center !important; } } @media (min-width: 1400px) { .float-xxl-start { float: left !important; } .float-xxl-end { float: right !important; } .float-xxl-none { float: none !important; } .d-xxl-inline { display: inline !important; } .d-xxl-inline-block { display: inline-block !important; } .d-xxl-block { display: block !important; } .d-xxl-grid { display: grid !important; } .d-xxl-table { display: table !important; } .d-xxl-table-row { display: table-row !important; } .d-xxl-table-cell { display: table-cell !important; } .d-xxl-flex { display: flex !important; } .d-xxl-inline-flex { display: inline-flex !important; } .d-xxl-none { display: none !important; } .flex-xxl-fill { flex: 1 1 auto !important; } .flex-xxl-row { flex-direction: row !important; } .flex-xxl-column { flex-direction: column !important; } .flex-xxl-row-reverse { flex-direction: row-reverse !important; } .flex-xxl-column-reverse { flex-direction: column-reverse !important; } .flex-xxl-grow-0 { flex-grow: 0 !important; } .flex-xxl-grow-1 { flex-grow: 1 !important; } .flex-xxl-shrink-0 { flex-shrink: 0 !important; } .flex-xxl-shrink-1 { flex-shrink: 1 !important; } .flex-xxl-wrap { flex-wrap: wrap !important; } .flex-xxl-nowrap { flex-wrap: nowrap !important; } .flex-xxl-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-xxl-0 { gap: 0 !important; } .gap-xxl-1 { gap: 0.25rem !important; } .gap-xxl-2 { gap: 0.5rem !important; } .gap-xxl-3 { gap: 1rem !important; } .gap-xxl-4 { gap: 1.5rem !important; } .gap-xxl-5 { gap: 3rem !important; } .justify-content-xxl-start { justify-content: flex-start !important; } .justify-content-xxl-end { justify-content: flex-end !important; } .justify-content-xxl-center { justify-content: center !important; } .justify-content-xxl-between { justify-content: space-between !important; } .justify-content-xxl-around { justify-content: space-around !important; } .justify-content-xxl-evenly { justify-content: space-evenly !important; } .align-items-xxl-start { align-items: flex-start !important; } .align-items-xxl-end { align-items: flex-end !important; } .align-items-xxl-center { align-items: center !important; } .align-items-xxl-baseline { align-items: baseline !important; } .align-items-xxl-stretch { align-items: stretch !important; } .align-content-xxl-start { align-content: flex-start !important; } .align-content-xxl-end { align-content: flex-end !important; } .align-content-xxl-center { align-content: center !important; } .align-content-xxl-between { align-content: space-between !important; } .align-content-xxl-around { align-content: space-around !important; } .align-content-xxl-stretch { align-content: stretch !important; } .align-self-xxl-auto { align-self: auto !important; } .align-self-xxl-start { align-self: flex-start !important; } .align-self-xxl-end { align-self: flex-end !important; } .align-self-xxl-center { align-self: center !important; } .align-self-xxl-baseline { align-self: baseline !important; } .align-self-xxl-stretch { align-self: stretch !important; } .order-xxl-first { order: -1 !important; } .order-xxl-0 { order: 0 !important; } .order-xxl-1 { order: 1 !important; } .order-xxl-2 { order: 2 !important; } .order-xxl-3 { order: 3 !important; } .order-xxl-4 { order: 4 !important; } .order-xxl-5 { order: 5 !important; } .order-xxl-last { order: 6 !important; } .m-xxl-0 { margin: 0 !important; } .m-xxl-1 { margin: 0.25rem !important; } .m-xxl-2 { margin: 0.5rem !important; } .m-xxl-3 { margin: 1rem !important; } .m-xxl-4 { margin: 1.5rem !important; } .m-xxl-5 { margin: 3rem !important; } .m-xxl-auto { margin: auto !important; } .mx-xxl-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-xxl-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-xxl-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-xxl-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-xxl-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-xxl-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-xxl-auto { margin-right: auto !important; margin-left: auto !important; } .my-xxl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xxl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xxl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xxl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xxl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xxl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xxl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xxl-0 { margin-top: 0 !important; } .mt-xxl-1 { margin-top: 0.25rem !important; } .mt-xxl-2 { margin-top: 0.5rem !important; } .mt-xxl-3 { margin-top: 1rem !important; } .mt-xxl-4 { margin-top: 1.5rem !important; } .mt-xxl-5 { margin-top: 3rem !important; } .mt-xxl-auto { margin-top: auto !important; } .me-xxl-0 { margin-right: 0 !important; } .me-xxl-1 { margin-right: 0.25rem !important; } .me-xxl-2 { margin-right: 0.5rem !important; } .me-xxl-3 { margin-right: 1rem !important; } .me-xxl-4 { margin-right: 1.5rem !important; } .me-xxl-5 { margin-right: 3rem !important; } .me-xxl-auto { margin-right: auto !important; } .mb-xxl-0 { margin-bottom: 0 !important; } .mb-xxl-1 { margin-bottom: 0.25rem !important; } .mb-xxl-2 { margin-bottom: 0.5rem !important; } .mb-xxl-3 { margin-bottom: 1rem !important; } .mb-xxl-4 { margin-bottom: 1.5rem !important; } .mb-xxl-5 { margin-bottom: 3rem !important; } .mb-xxl-auto { margin-bottom: auto !important; } .ms-xxl-0 { margin-left: 0 !important; } .ms-xxl-1 { margin-left: 0.25rem !important; } .ms-xxl-2 { margin-left: 0.5rem !important; } .ms-xxl-3 { margin-left: 1rem !important; } .ms-xxl-4 { margin-left: 1.5rem !important; } .ms-xxl-5 { margin-left: 3rem !important; } .ms-xxl-auto { margin-left: auto !important; } .p-xxl-0 { padding: 0 !important; } .p-xxl-1 { padding: 0.25rem !important; } .p-xxl-2 { padding: 0.5rem !important; } .p-xxl-3 { padding: 1rem !important; } .p-xxl-4 { padding: 1.5rem !important; } .p-xxl-5 { padding: 3rem !important; } .px-xxl-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-xxl-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-xxl-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-xxl-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-xxl-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-xxl-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-xxl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xxl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xxl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xxl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xxl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xxl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xxl-0 { padding-top: 0 !important; } .pt-xxl-1 { padding-top: 0.25rem !important; } .pt-xxl-2 { padding-top: 0.5rem !important; } .pt-xxl-3 { padding-top: 1rem !important; } .pt-xxl-4 { padding-top: 1.5rem !important; } .pt-xxl-5 { padding-top: 3rem !important; } .pe-xxl-0 { padding-right: 0 !important; } .pe-xxl-1 { padding-right: 0.25rem !important; } .pe-xxl-2 { padding-right: 0.5rem !important; } .pe-xxl-3 { padding-right: 1rem !important; } .pe-xxl-4 { padding-right: 1.5rem !important; } .pe-xxl-5 { padding-right: 3rem !important; } .pb-xxl-0 { padding-bottom: 0 !important; } .pb-xxl-1 { padding-bottom: 0.25rem !important; } .pb-xxl-2 { padding-bottom: 0.5rem !important; } .pb-xxl-3 { padding-bottom: 1rem !important; } .pb-xxl-4 { padding-bottom: 1.5rem !important; } .pb-xxl-5 { padding-bottom: 3rem !important; } .ps-xxl-0 { padding-left: 0 !important; } .ps-xxl-1 { padding-left: 0.25rem !important; } .ps-xxl-2 { padding-left: 0.5rem !important; } .ps-xxl-3 { padding-left: 1rem !important; } .ps-xxl-4 { padding-left: 1.5rem !important; } .ps-xxl-5 { padding-left: 3rem !important; } .text-xxl-start { text-align: left !important; } .text-xxl-end { text-align: right !important; } .text-xxl-center { text-align: center !important; } } @media (min-width: 1200px) { .fs-1 { font-size: 2.5rem !important; } .fs-2 { font-size: 2rem !important; } .fs-3 { font-size: 1.75rem !important; } .fs-4 { font-size: 1.5rem !important; } } @media print { .d-print-inline { display: inline !important; } .d-print-inline-block { display: inline-block !important; } .d-print-block { display: block !important; } .d-print-grid { display: grid !important; } .d-print-table { display: table !important; } .d-print-table-row { display: table-row !important; } .d-print-table-cell { display: table-cell !important; } .d-print-flex { display: flex !important; } .d-print-inline-flex { display: inline-flex !important; } .d-print-none { display: none !important; } } /*# sourceMappingURL=bootstrap-utilities.css.map */ ================================================ FILE: examples/tailwind-and-bootstrap-example/css-modules/bootstrap/bootstrap-utilities.rtl.css ================================================ /*! * Bootstrap Utilities v5.1.3 (https://getbootstrap.com/) * Copyright 2011-2021 The Bootstrap Authors * Copyright 2011-2021 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ .clearfix::after { display: block; clear: both; content: ""; } .link-primary { color: #0d6efd; } .link-primary:hover, .link-primary:focus { color: #0a58ca; } .link-secondary { color: #6c757d; } .link-secondary:hover, .link-secondary:focus { color: #565e64; } .link-success { color: #198754; } .link-success:hover, .link-success:focus { color: #146c43; } .link-info { color: #0dcaf0; } .link-info:hover, .link-info:focus { color: #3dd5f3; } .link-warning { color: #ffc107; } .link-warning:hover, .link-warning:focus { color: #ffcd39; } .link-danger { color: #dc3545; } .link-danger:hover, .link-danger:focus { color: #b02a37; } .link-light { color: #f8f9fa; } .link-light:hover, .link-light:focus { color: #f9fafb; } .link-dark { color: #212529; } .link-dark:hover, .link-dark:focus { color: #1a1e21; } .ratio { position: relative; width: 100%; } .ratio::before { display: block; padding-top: var(--bs-aspect-ratio); content: ""; } .ratio > * { position: absolute; top: 0; right: 0; width: 100%; height: 100%; } .ratio-1x1 { --bs-aspect-ratio: 100%; } .ratio-4x3 { --bs-aspect-ratio: 75%; } .ratio-16x9 { --bs-aspect-ratio: 56.25%; } .ratio-21x9 { --bs-aspect-ratio: 42.8571428571%; } .fixed-top { position: fixed; top: 0; left: 0; right: 0; z-index: 1030; } .fixed-bottom { position: fixed; left: 0; bottom: 0; right: 0; z-index: 1030; } .sticky-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } @media (min-width: 576px) { .sticky-sm-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 768px) { .sticky-md-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 992px) { .sticky-lg-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 1200px) { .sticky-xl-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 1400px) { .sticky-xxl-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } .hstack { display: flex; flex-direction: row; align-items: center; align-self: stretch; } .vstack { display: flex; flex: 1 1 auto; flex-direction: column; align-self: stretch; } .visually-hidden, .visually-hidden-focusable:not(:focus):not(:focus-within) { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; border: 0 !important; } .stretched-link::after { position: absolute; top: 0; left: 0; bottom: 0; right: 0; z-index: 1; content: ""; } .text-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .vr { display: inline-block; align-self: stretch; width: 1px; min-height: 1em; background-color: currentColor; opacity: 0.25; } .align-baseline { vertical-align: baseline !important; } .align-top { vertical-align: top !important; } .align-middle { vertical-align: middle !important; } .align-bottom { vertical-align: bottom !important; } .align-text-bottom { vertical-align: text-bottom !important; } .align-text-top { vertical-align: text-top !important; } .float-start { float: right !important; } .float-end { float: left !important; } .float-none { float: none !important; } .opacity-0 { opacity: 0 !important; } .opacity-25 { opacity: 0.25 !important; } .opacity-50 { opacity: 0.5 !important; } .opacity-75 { opacity: 0.75 !important; } .opacity-100 { opacity: 1 !important; } .overflow-auto { overflow: auto !important; } .overflow-hidden { overflow: hidden !important; } .overflow-visible { overflow: visible !important; } .overflow-scroll { overflow: scroll !important; } .d-inline { display: inline !important; } .d-inline-block { display: inline-block !important; } .d-block { display: block !important; } .d-grid { display: grid !important; } .d-table { display: table !important; } .d-table-row { display: table-row !important; } .d-table-cell { display: table-cell !important; } .d-flex { display: flex !important; } .d-inline-flex { display: inline-flex !important; } .d-none { display: none !important; } .shadow { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; } .shadow-sm { box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; } .shadow-lg { box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; } .shadow-none { box-shadow: none !important; } .position-static { position: static !important; } .position-relative { position: relative !important; } .position-absolute { position: absolute !important; } .position-fixed { position: fixed !important; } .position-sticky { position: -webkit-sticky !important; position: sticky !important; } .top-0 { top: 0 !important; } .top-50 { top: 50% !important; } .top-100 { top: 100% !important; } .bottom-0 { bottom: 0 !important; } .bottom-50 { bottom: 50% !important; } .bottom-100 { bottom: 100% !important; } .start-0 { right: 0 !important; } .start-50 { right: 50% !important; } .start-100 { right: 100% !important; } .end-0 { left: 0 !important; } .end-50 { left: 50% !important; } .end-100 { left: 100% !important; } .translate-middle { transform: translate(50%, -50%) !important; } .translate-middle-x { transform: translateX(50%) !important; } .translate-middle-y { transform: translateY(-50%) !important; } .border { border: 1px solid #dee2e6 !important; } .border-0 { border: 0 !important; } .border-top { border-top: 1px solid #dee2e6 !important; } .border-top-0 { border-top: 0 !important; } .border-end { border-left: 1px solid #dee2e6 !important; } .border-end-0 { border-left: 0 !important; } .border-bottom { border-bottom: 1px solid #dee2e6 !important; } .border-bottom-0 { border-bottom: 0 !important; } .border-start { border-right: 1px solid #dee2e6 !important; } .border-start-0 { border-right: 0 !important; } .border-primary { border-color: #0d6efd !important; } .border-secondary { border-color: #6c757d !important; } .border-success { border-color: #198754 !important; } .border-info { border-color: #0dcaf0 !important; } .border-warning { border-color: #ffc107 !important; } .border-danger { border-color: #dc3545 !important; } .border-light { border-color: #f8f9fa !important; } .border-dark { border-color: #212529 !important; } .border-white { border-color: #fff !important; } .border-1 { border-width: 1px !important; } .border-2 { border-width: 2px !important; } .border-3 { border-width: 3px !important; } .border-4 { border-width: 4px !important; } .border-5 { border-width: 5px !important; } .w-25 { width: 25% !important; } .w-50 { width: 50% !important; } .w-75 { width: 75% !important; } .w-100 { width: 100% !important; } .w-auto { width: auto !important; } .mw-100 { max-width: 100% !important; } .vw-100 { width: 100vw !important; } .min-vw-100 { min-width: 100vw !important; } .h-25 { height: 25% !important; } .h-50 { height: 50% !important; } .h-75 { height: 75% !important; } .h-100 { height: 100% !important; } .h-auto { height: auto !important; } .mh-100 { max-height: 100% !important; } .vh-100 { height: 100vh !important; } .min-vh-100 { min-height: 100vh !important; } .flex-fill { flex: 1 1 auto !important; } .flex-row { flex-direction: row !important; } .flex-column { flex-direction: column !important; } .flex-row-reverse { flex-direction: row-reverse !important; } .flex-column-reverse { flex-direction: column-reverse !important; } .flex-grow-0 { flex-grow: 0 !important; } .flex-grow-1 { flex-grow: 1 !important; } .flex-shrink-0 { flex-shrink: 0 !important; } .flex-shrink-1 { flex-shrink: 1 !important; } .flex-wrap { flex-wrap: wrap !important; } .flex-nowrap { flex-wrap: nowrap !important; } .flex-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-0 { gap: 0 !important; } .gap-1 { gap: 0.25rem !important; } .gap-2 { gap: 0.5rem !important; } .gap-3 { gap: 1rem !important; } .gap-4 { gap: 1.5rem !important; } .gap-5 { gap: 3rem !important; } .justify-content-start { justify-content: flex-start !important; } .justify-content-end { justify-content: flex-end !important; } .justify-content-center { justify-content: center !important; } .justify-content-between { justify-content: space-between !important; } .justify-content-around { justify-content: space-around !important; } .justify-content-evenly { justify-content: space-evenly !important; } .align-items-start { align-items: flex-start !important; } .align-items-end { align-items: flex-end !important; } .align-items-center { align-items: center !important; } .align-items-baseline { align-items: baseline !important; } .align-items-stretch { align-items: stretch !important; } .align-content-start { align-content: flex-start !important; } .align-content-end { align-content: flex-end !important; } .align-content-center { align-content: center !important; } .align-content-between { align-content: space-between !important; } .align-content-around { align-content: space-around !important; } .align-content-stretch { align-content: stretch !important; } .align-self-auto { align-self: auto !important; } .align-self-start { align-self: flex-start !important; } .align-self-end { align-self: flex-end !important; } .align-self-center { align-self: center !important; } .align-self-baseline { align-self: baseline !important; } .align-self-stretch { align-self: stretch !important; } .order-first { order: -1 !important; } .order-0 { order: 0 !important; } .order-1 { order: 1 !important; } .order-2 { order: 2 !important; } .order-3 { order: 3 !important; } .order-4 { order: 4 !important; } .order-5 { order: 5 !important; } .order-last { order: 6 !important; } .m-0 { margin: 0 !important; } .m-1 { margin: 0.25rem !important; } .m-2 { margin: 0.5rem !important; } .m-3 { margin: 1rem !important; } .m-4 { margin: 1.5rem !important; } .m-5 { margin: 3rem !important; } .m-auto { margin: auto !important; } .mx-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-auto { margin-left: auto !important; margin-right: auto !important; } .my-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-0 { margin-top: 0 !important; } .mt-1 { margin-top: 0.25rem !important; } .mt-2 { margin-top: 0.5rem !important; } .mt-3 { margin-top: 1rem !important; } .mt-4 { margin-top: 1.5rem !important; } .mt-5 { margin-top: 3rem !important; } .mt-auto { margin-top: auto !important; } .me-0 { margin-left: 0 !important; } .me-1 { margin-left: 0.25rem !important; } .me-2 { margin-left: 0.5rem !important; } .me-3 { margin-left: 1rem !important; } .me-4 { margin-left: 1.5rem !important; } .me-5 { margin-left: 3rem !important; } .me-auto { margin-left: auto !important; } .mb-0 { margin-bottom: 0 !important; } .mb-1 { margin-bottom: 0.25rem !important; } .mb-2 { margin-bottom: 0.5rem !important; } .mb-3 { margin-bottom: 1rem !important; } .mb-4 { margin-bottom: 1.5rem !important; } .mb-5 { margin-bottom: 3rem !important; } .mb-auto { margin-bottom: auto !important; } .ms-0 { margin-right: 0 !important; } .ms-1 { margin-right: 0.25rem !important; } .ms-2 { margin-right: 0.5rem !important; } .ms-3 { margin-right: 1rem !important; } .ms-4 { margin-right: 1.5rem !important; } .ms-5 { margin-right: 3rem !important; } .ms-auto { margin-right: auto !important; } .p-0 { padding: 0 !important; } .p-1 { padding: 0.25rem !important; } .p-2 { padding: 0.5rem !important; } .p-3 { padding: 1rem !important; } .p-4 { padding: 1.5rem !important; } .p-5 { padding: 3rem !important; } .px-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-0 { padding-top: 0 !important; } .pt-1 { padding-top: 0.25rem !important; } .pt-2 { padding-top: 0.5rem !important; } .pt-3 { padding-top: 1rem !important; } .pt-4 { padding-top: 1.5rem !important; } .pt-5 { padding-top: 3rem !important; } .pe-0 { padding-left: 0 !important; } .pe-1 { padding-left: 0.25rem !important; } .pe-2 { padding-left: 0.5rem !important; } .pe-3 { padding-left: 1rem !important; } .pe-4 { padding-left: 1.5rem !important; } .pe-5 { padding-left: 3rem !important; } .pb-0 { padding-bottom: 0 !important; } .pb-1 { padding-bottom: 0.25rem !important; } .pb-2 { padding-bottom: 0.5rem !important; } .pb-3 { padding-bottom: 1rem !important; } .pb-4 { padding-bottom: 1.5rem !important; } .pb-5 { padding-bottom: 3rem !important; } .ps-0 { padding-right: 0 !important; } .ps-1 { padding-right: 0.25rem !important; } .ps-2 { padding-right: 0.5rem !important; } .ps-3 { padding-right: 1rem !important; } .ps-4 { padding-right: 1.5rem !important; } .ps-5 { padding-right: 3rem !important; } .font-monospace { font-family: var(--bs-font-monospace) !important; } .fs-1 { font-size: calc(1.375rem + 1.5vw) !important; } .fs-2 { font-size: calc(1.325rem + 0.9vw) !important; } .fs-3 { font-size: calc(1.3rem + 0.6vw) !important; } .fs-4 { font-size: calc(1.275rem + 0.3vw) !important; } .fs-5 { font-size: 1.25rem !important; } .fs-6 { font-size: 1rem !important; } .fst-italic { font-style: italic !important; } .fst-normal { font-style: normal !important; } .fw-light { font-weight: 300 !important; } .fw-lighter { font-weight: lighter !important; } .fw-normal { font-weight: 400 !important; } .fw-bold { font-weight: 700 !important; } .fw-bolder { font-weight: bolder !important; } .lh-1 { line-height: 1 !important; } .lh-sm { line-height: 1.25 !important; } .lh-base { line-height: 1.5 !important; } .lh-lg { line-height: 2 !important; } .text-start { text-align: right !important; } .text-end { text-align: left !important; } .text-center { text-align: center !important; } .text-decoration-none { text-decoration: none !important; } .text-decoration-underline { text-decoration: underline !important; } .text-decoration-line-through { text-decoration: line-through !important; } .text-lowercase { text-transform: lowercase !important; } .text-uppercase { text-transform: uppercase !important; } .text-capitalize { text-transform: capitalize !important; } .text-wrap { white-space: normal !important; } .text-nowrap { white-space: nowrap !important; } .text-primary { --bs-text-opacity: 1; color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; } .text-secondary { --bs-text-opacity: 1; color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; } .text-success { --bs-text-opacity: 1; color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; } .text-info { --bs-text-opacity: 1; color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; } .text-warning { --bs-text-opacity: 1; color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; } .text-danger { --bs-text-opacity: 1; color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; } .text-light { --bs-text-opacity: 1; color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; } .text-dark { --bs-text-opacity: 1; color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; } .text-black { --bs-text-opacity: 1; color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; } .text-white { --bs-text-opacity: 1; color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; } .text-body { --bs-text-opacity: 1; color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; } .text-muted { --bs-text-opacity: 1; color: #6c757d !important; } .text-black-50 { --bs-text-opacity: 1; color: rgba(0, 0, 0, 0.5) !important; } .text-white-50 { --bs-text-opacity: 1; color: rgba(255, 255, 255, 0.5) !important; } .text-reset { --bs-text-opacity: 1; color: inherit !important; } .text-opacity-25 { --bs-text-opacity: 0.25; } .text-opacity-50 { --bs-text-opacity: 0.5; } .text-opacity-75 { --bs-text-opacity: 0.75; } .text-opacity-100 { --bs-text-opacity: 1; } .bg-primary { --bs-bg-opacity: 1; background-color: rgba( var(--bs-primary-rgb), var(--bs-bg-opacity) ) !important; } .bg-secondary { --bs-bg-opacity: 1; background-color: rgba( var(--bs-secondary-rgb), var(--bs-bg-opacity) ) !important; } .bg-success { --bs-bg-opacity: 1; background-color: rgba( var(--bs-success-rgb), var(--bs-bg-opacity) ) !important; } .bg-info { --bs-bg-opacity: 1; background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; } .bg-warning { --bs-bg-opacity: 1; background-color: rgba( var(--bs-warning-rgb), var(--bs-bg-opacity) ) !important; } .bg-danger { --bs-bg-opacity: 1; background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; } .bg-light { --bs-bg-opacity: 1; background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; } .bg-dark { --bs-bg-opacity: 1; background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } .bg-black { --bs-bg-opacity: 1; background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; } .bg-white { --bs-bg-opacity: 1; background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; } .bg-body { --bs-bg-opacity: 1; background-color: rgba( var(--bs-body-bg-rgb), var(--bs-bg-opacity) ) !important; } .bg-transparent { --bs-bg-opacity: 1; background-color: transparent !important; } .bg-opacity-10 { --bs-bg-opacity: 0.1; } .bg-opacity-25 { --bs-bg-opacity: 0.25; } .bg-opacity-50 { --bs-bg-opacity: 0.5; } .bg-opacity-75 { --bs-bg-opacity: 0.75; } .bg-opacity-100 { --bs-bg-opacity: 1; } .bg-gradient { background-image: var(--bs-gradient) !important; } .user-select-all { -webkit-user-select: all !important; -moz-user-select: all !important; user-select: all !important; } .user-select-auto { -webkit-user-select: auto !important; -moz-user-select: auto !important; user-select: auto !important; } .user-select-none { -webkit-user-select: none !important; -moz-user-select: none !important; user-select: none !important; } .pe-none { pointer-events: none !important; } .pe-auto { pointer-events: auto !important; } .rounded { border-radius: 0.25rem !important; } .rounded-0 { border-radius: 0 !important; } .rounded-1 { border-radius: 0.2rem !important; } .rounded-2 { border-radius: 0.25rem !important; } .rounded-3 { border-radius: 0.3rem !important; } .rounded-circle { border-radius: 50% !important; } .rounded-pill { border-radius: 50rem !important; } .rounded-top { border-top-right-radius: 0.25rem !important; border-top-left-radius: 0.25rem !important; } .rounded-end { border-top-left-radius: 0.25rem !important; border-bottom-left-radius: 0.25rem !important; } .rounded-bottom { border-bottom-left-radius: 0.25rem !important; border-bottom-right-radius: 0.25rem !important; } .rounded-start { border-bottom-right-radius: 0.25rem !important; border-top-right-radius: 0.25rem !important; } .visible { visibility: visible !important; } .invisible { visibility: hidden !important; } @media (min-width: 576px) { .float-sm-start { float: right !important; } .float-sm-end { float: left !important; } .float-sm-none { float: none !important; } .d-sm-inline { display: inline !important; } .d-sm-inline-block { display: inline-block !important; } .d-sm-block { display: block !important; } .d-sm-grid { display: grid !important; } .d-sm-table { display: table !important; } .d-sm-table-row { display: table-row !important; } .d-sm-table-cell { display: table-cell !important; } .d-sm-flex { display: flex !important; } .d-sm-inline-flex { display: inline-flex !important; } .d-sm-none { display: none !important; } .flex-sm-fill { flex: 1 1 auto !important; } .flex-sm-row { flex-direction: row !important; } .flex-sm-column { flex-direction: column !important; } .flex-sm-row-reverse { flex-direction: row-reverse !important; } .flex-sm-column-reverse { flex-direction: column-reverse !important; } .flex-sm-grow-0 { flex-grow: 0 !important; } .flex-sm-grow-1 { flex-grow: 1 !important; } .flex-sm-shrink-0 { flex-shrink: 0 !important; } .flex-sm-shrink-1 { flex-shrink: 1 !important; } .flex-sm-wrap { flex-wrap: wrap !important; } .flex-sm-nowrap { flex-wrap: nowrap !important; } .flex-sm-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-sm-0 { gap: 0 !important; } .gap-sm-1 { gap: 0.25rem !important; } .gap-sm-2 { gap: 0.5rem !important; } .gap-sm-3 { gap: 1rem !important; } .gap-sm-4 { gap: 1.5rem !important; } .gap-sm-5 { gap: 3rem !important; } .justify-content-sm-start { justify-content: flex-start !important; } .justify-content-sm-end { justify-content: flex-end !important; } .justify-content-sm-center { justify-content: center !important; } .justify-content-sm-between { justify-content: space-between !important; } .justify-content-sm-around { justify-content: space-around !important; } .justify-content-sm-evenly { justify-content: space-evenly !important; } .align-items-sm-start { align-items: flex-start !important; } .align-items-sm-end { align-items: flex-end !important; } .align-items-sm-center { align-items: center !important; } .align-items-sm-baseline { align-items: baseline !important; } .align-items-sm-stretch { align-items: stretch !important; } .align-content-sm-start { align-content: flex-start !important; } .align-content-sm-end { align-content: flex-end !important; } .align-content-sm-center { align-content: center !important; } .align-content-sm-between { align-content: space-between !important; } .align-content-sm-around { align-content: space-around !important; } .align-content-sm-stretch { align-content: stretch !important; } .align-self-sm-auto { align-self: auto !important; } .align-self-sm-start { align-self: flex-start !important; } .align-self-sm-end { align-self: flex-end !important; } .align-self-sm-center { align-self: center !important; } .align-self-sm-baseline { align-self: baseline !important; } .align-self-sm-stretch { align-self: stretch !important; } .order-sm-first { order: -1 !important; } .order-sm-0 { order: 0 !important; } .order-sm-1 { order: 1 !important; } .order-sm-2 { order: 2 !important; } .order-sm-3 { order: 3 !important; } .order-sm-4 { order: 4 !important; } .order-sm-5 { order: 5 !important; } .order-sm-last { order: 6 !important; } .m-sm-0 { margin: 0 !important; } .m-sm-1 { margin: 0.25rem !important; } .m-sm-2 { margin: 0.5rem !important; } .m-sm-3 { margin: 1rem !important; } .m-sm-4 { margin: 1.5rem !important; } .m-sm-5 { margin: 3rem !important; } .m-sm-auto { margin: auto !important; } .mx-sm-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-sm-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-sm-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-sm-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-sm-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-sm-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-sm-auto { margin-left: auto !important; margin-right: auto !important; } .my-sm-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-sm-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-sm-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-sm-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-sm-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-sm-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-sm-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-sm-0 { margin-top: 0 !important; } .mt-sm-1 { margin-top: 0.25rem !important; } .mt-sm-2 { margin-top: 0.5rem !important; } .mt-sm-3 { margin-top: 1rem !important; } .mt-sm-4 { margin-top: 1.5rem !important; } .mt-sm-5 { margin-top: 3rem !important; } .mt-sm-auto { margin-top: auto !important; } .me-sm-0 { margin-left: 0 !important; } .me-sm-1 { margin-left: 0.25rem !important; } .me-sm-2 { margin-left: 0.5rem !important; } .me-sm-3 { margin-left: 1rem !important; } .me-sm-4 { margin-left: 1.5rem !important; } .me-sm-5 { margin-left: 3rem !important; } .me-sm-auto { margin-left: auto !important; } .mb-sm-0 { margin-bottom: 0 !important; } .mb-sm-1 { margin-bottom: 0.25rem !important; } .mb-sm-2 { margin-bottom: 0.5rem !important; } .mb-sm-3 { margin-bottom: 1rem !important; } .mb-sm-4 { margin-bottom: 1.5rem !important; } .mb-sm-5 { margin-bottom: 3rem !important; } .mb-sm-auto { margin-bottom: auto !important; } .ms-sm-0 { margin-right: 0 !important; } .ms-sm-1 { margin-right: 0.25rem !important; } .ms-sm-2 { margin-right: 0.5rem !important; } .ms-sm-3 { margin-right: 1rem !important; } .ms-sm-4 { margin-right: 1.5rem !important; } .ms-sm-5 { margin-right: 3rem !important; } .ms-sm-auto { margin-right: auto !important; } .p-sm-0 { padding: 0 !important; } .p-sm-1 { padding: 0.25rem !important; } .p-sm-2 { padding: 0.5rem !important; } .p-sm-3 { padding: 1rem !important; } .p-sm-4 { padding: 1.5rem !important; } .p-sm-5 { padding: 3rem !important; } .px-sm-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-sm-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-sm-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-sm-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-sm-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-sm-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-sm-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-sm-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-sm-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-sm-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-sm-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-sm-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-sm-0 { padding-top: 0 !important; } .pt-sm-1 { padding-top: 0.25rem !important; } .pt-sm-2 { padding-top: 0.5rem !important; } .pt-sm-3 { padding-top: 1rem !important; } .pt-sm-4 { padding-top: 1.5rem !important; } .pt-sm-5 { padding-top: 3rem !important; } .pe-sm-0 { padding-left: 0 !important; } .pe-sm-1 { padding-left: 0.25rem !important; } .pe-sm-2 { padding-left: 0.5rem !important; } .pe-sm-3 { padding-left: 1rem !important; } .pe-sm-4 { padding-left: 1.5rem !important; } .pe-sm-5 { padding-left: 3rem !important; } .pb-sm-0 { padding-bottom: 0 !important; } .pb-sm-1 { padding-bottom: 0.25rem !important; } .pb-sm-2 { padding-bottom: 0.5rem !important; } .pb-sm-3 { padding-bottom: 1rem !important; } .pb-sm-4 { padding-bottom: 1.5rem !important; } .pb-sm-5 { padding-bottom: 3rem !important; } .ps-sm-0 { padding-right: 0 !important; } .ps-sm-1 { padding-right: 0.25rem !important; } .ps-sm-2 { padding-right: 0.5rem !important; } .ps-sm-3 { padding-right: 1rem !important; } .ps-sm-4 { padding-right: 1.5rem !important; } .ps-sm-5 { padding-right: 3rem !important; } .text-sm-start { text-align: right !important; } .text-sm-end { text-align: left !important; } .text-sm-center { text-align: center !important; } } @media (min-width: 768px) { .float-md-start { float: right !important; } .float-md-end { float: left !important; } .float-md-none { float: none !important; } .d-md-inline { display: inline !important; } .d-md-inline-block { display: inline-block !important; } .d-md-block { display: block !important; } .d-md-grid { display: grid !important; } .d-md-table { display: table !important; } .d-md-table-row { display: table-row !important; } .d-md-table-cell { display: table-cell !important; } .d-md-flex { display: flex !important; } .d-md-inline-flex { display: inline-flex !important; } .d-md-none { display: none !important; } .flex-md-fill { flex: 1 1 auto !important; } .flex-md-row { flex-direction: row !important; } .flex-md-column { flex-direction: column !important; } .flex-md-row-reverse { flex-direction: row-reverse !important; } .flex-md-column-reverse { flex-direction: column-reverse !important; } .flex-md-grow-0 { flex-grow: 0 !important; } .flex-md-grow-1 { flex-grow: 1 !important; } .flex-md-shrink-0 { flex-shrink: 0 !important; } .flex-md-shrink-1 { flex-shrink: 1 !important; } .flex-md-wrap { flex-wrap: wrap !important; } .flex-md-nowrap { flex-wrap: nowrap !important; } .flex-md-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-md-0 { gap: 0 !important; } .gap-md-1 { gap: 0.25rem !important; } .gap-md-2 { gap: 0.5rem !important; } .gap-md-3 { gap: 1rem !important; } .gap-md-4 { gap: 1.5rem !important; } .gap-md-5 { gap: 3rem !important; } .justify-content-md-start { justify-content: flex-start !important; } .justify-content-md-end { justify-content: flex-end !important; } .justify-content-md-center { justify-content: center !important; } .justify-content-md-between { justify-content: space-between !important; } .justify-content-md-around { justify-content: space-around !important; } .justify-content-md-evenly { justify-content: space-evenly !important; } .align-items-md-start { align-items: flex-start !important; } .align-items-md-end { align-items: flex-end !important; } .align-items-md-center { align-items: center !important; } .align-items-md-baseline { align-items: baseline !important; } .align-items-md-stretch { align-items: stretch !important; } .align-content-md-start { align-content: flex-start !important; } .align-content-md-end { align-content: flex-end !important; } .align-content-md-center { align-content: center !important; } .align-content-md-between { align-content: space-between !important; } .align-content-md-around { align-content: space-around !important; } .align-content-md-stretch { align-content: stretch !important; } .align-self-md-auto { align-self: auto !important; } .align-self-md-start { align-self: flex-start !important; } .align-self-md-end { align-self: flex-end !important; } .align-self-md-center { align-self: center !important; } .align-self-md-baseline { align-self: baseline !important; } .align-self-md-stretch { align-self: stretch !important; } .order-md-first { order: -1 !important; } .order-md-0 { order: 0 !important; } .order-md-1 { order: 1 !important; } .order-md-2 { order: 2 !important; } .order-md-3 { order: 3 !important; } .order-md-4 { order: 4 !important; } .order-md-5 { order: 5 !important; } .order-md-last { order: 6 !important; } .m-md-0 { margin: 0 !important; } .m-md-1 { margin: 0.25rem !important; } .m-md-2 { margin: 0.5rem !important; } .m-md-3 { margin: 1rem !important; } .m-md-4 { margin: 1.5rem !important; } .m-md-5 { margin: 3rem !important; } .m-md-auto { margin: auto !important; } .mx-md-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-md-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-md-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-md-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-md-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-md-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-md-auto { margin-left: auto !important; margin-right: auto !important; } .my-md-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-md-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-md-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-md-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-md-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-md-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-md-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-md-0 { margin-top: 0 !important; } .mt-md-1 { margin-top: 0.25rem !important; } .mt-md-2 { margin-top: 0.5rem !important; } .mt-md-3 { margin-top: 1rem !important; } .mt-md-4 { margin-top: 1.5rem !important; } .mt-md-5 { margin-top: 3rem !important; } .mt-md-auto { margin-top: auto !important; } .me-md-0 { margin-left: 0 !important; } .me-md-1 { margin-left: 0.25rem !important; } .me-md-2 { margin-left: 0.5rem !important; } .me-md-3 { margin-left: 1rem !important; } .me-md-4 { margin-left: 1.5rem !important; } .me-md-5 { margin-left: 3rem !important; } .me-md-auto { margin-left: auto !important; } .mb-md-0 { margin-bottom: 0 !important; } .mb-md-1 { margin-bottom: 0.25rem !important; } .mb-md-2 { margin-bottom: 0.5rem !important; } .mb-md-3 { margin-bottom: 1rem !important; } .mb-md-4 { margin-bottom: 1.5rem !important; } .mb-md-5 { margin-bottom: 3rem !important; } .mb-md-auto { margin-bottom: auto !important; } .ms-md-0 { margin-right: 0 !important; } .ms-md-1 { margin-right: 0.25rem !important; } .ms-md-2 { margin-right: 0.5rem !important; } .ms-md-3 { margin-right: 1rem !important; } .ms-md-4 { margin-right: 1.5rem !important; } .ms-md-5 { margin-right: 3rem !important; } .ms-md-auto { margin-right: auto !important; } .p-md-0 { padding: 0 !important; } .p-md-1 { padding: 0.25rem !important; } .p-md-2 { padding: 0.5rem !important; } .p-md-3 { padding: 1rem !important; } .p-md-4 { padding: 1.5rem !important; } .p-md-5 { padding: 3rem !important; } .px-md-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-md-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-md-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-md-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-md-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-md-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-md-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-md-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-md-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-md-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-md-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-md-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-md-0 { padding-top: 0 !important; } .pt-md-1 { padding-top: 0.25rem !important; } .pt-md-2 { padding-top: 0.5rem !important; } .pt-md-3 { padding-top: 1rem !important; } .pt-md-4 { padding-top: 1.5rem !important; } .pt-md-5 { padding-top: 3rem !important; } .pe-md-0 { padding-left: 0 !important; } .pe-md-1 { padding-left: 0.25rem !important; } .pe-md-2 { padding-left: 0.5rem !important; } .pe-md-3 { padding-left: 1rem !important; } .pe-md-4 { padding-left: 1.5rem !important; } .pe-md-5 { padding-left: 3rem !important; } .pb-md-0 { padding-bottom: 0 !important; } .pb-md-1 { padding-bottom: 0.25rem !important; } .pb-md-2 { padding-bottom: 0.5rem !important; } .pb-md-3 { padding-bottom: 1rem !important; } .pb-md-4 { padding-bottom: 1.5rem !important; } .pb-md-5 { padding-bottom: 3rem !important; } .ps-md-0 { padding-right: 0 !important; } .ps-md-1 { padding-right: 0.25rem !important; } .ps-md-2 { padding-right: 0.5rem !important; } .ps-md-3 { padding-right: 1rem !important; } .ps-md-4 { padding-right: 1.5rem !important; } .ps-md-5 { padding-right: 3rem !important; } .text-md-start { text-align: right !important; } .text-md-end { text-align: left !important; } .text-md-center { text-align: center !important; } } @media (min-width: 992px) { .float-lg-start { float: right !important; } .float-lg-end { float: left !important; } .float-lg-none { float: none !important; } .d-lg-inline { display: inline !important; } .d-lg-inline-block { display: inline-block !important; } .d-lg-block { display: block !important; } .d-lg-grid { display: grid !important; } .d-lg-table { display: table !important; } .d-lg-table-row { display: table-row !important; } .d-lg-table-cell { display: table-cell !important; } .d-lg-flex { display: flex !important; } .d-lg-inline-flex { display: inline-flex !important; } .d-lg-none { display: none !important; } .flex-lg-fill { flex: 1 1 auto !important; } .flex-lg-row { flex-direction: row !important; } .flex-lg-column { flex-direction: column !important; } .flex-lg-row-reverse { flex-direction: row-reverse !important; } .flex-lg-column-reverse { flex-direction: column-reverse !important; } .flex-lg-grow-0 { flex-grow: 0 !important; } .flex-lg-grow-1 { flex-grow: 1 !important; } .flex-lg-shrink-0 { flex-shrink: 0 !important; } .flex-lg-shrink-1 { flex-shrink: 1 !important; } .flex-lg-wrap { flex-wrap: wrap !important; } .flex-lg-nowrap { flex-wrap: nowrap !important; } .flex-lg-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-lg-0 { gap: 0 !important; } .gap-lg-1 { gap: 0.25rem !important; } .gap-lg-2 { gap: 0.5rem !important; } .gap-lg-3 { gap: 1rem !important; } .gap-lg-4 { gap: 1.5rem !important; } .gap-lg-5 { gap: 3rem !important; } .justify-content-lg-start { justify-content: flex-start !important; } .justify-content-lg-end { justify-content: flex-end !important; } .justify-content-lg-center { justify-content: center !important; } .justify-content-lg-between { justify-content: space-between !important; } .justify-content-lg-around { justify-content: space-around !important; } .justify-content-lg-evenly { justify-content: space-evenly !important; } .align-items-lg-start { align-items: flex-start !important; } .align-items-lg-end { align-items: flex-end !important; } .align-items-lg-center { align-items: center !important; } .align-items-lg-baseline { align-items: baseline !important; } .align-items-lg-stretch { align-items: stretch !important; } .align-content-lg-start { align-content: flex-start !important; } .align-content-lg-end { align-content: flex-end !important; } .align-content-lg-center { align-content: center !important; } .align-content-lg-between { align-content: space-between !important; } .align-content-lg-around { align-content: space-around !important; } .align-content-lg-stretch { align-content: stretch !important; } .align-self-lg-auto { align-self: auto !important; } .align-self-lg-start { align-self: flex-start !important; } .align-self-lg-end { align-self: flex-end !important; } .align-self-lg-center { align-self: center !important; } .align-self-lg-baseline { align-self: baseline !important; } .align-self-lg-stretch { align-self: stretch !important; } .order-lg-first { order: -1 !important; } .order-lg-0 { order: 0 !important; } .order-lg-1 { order: 1 !important; } .order-lg-2 { order: 2 !important; } .order-lg-3 { order: 3 !important; } .order-lg-4 { order: 4 !important; } .order-lg-5 { order: 5 !important; } .order-lg-last { order: 6 !important; } .m-lg-0 { margin: 0 !important; } .m-lg-1 { margin: 0.25rem !important; } .m-lg-2 { margin: 0.5rem !important; } .m-lg-3 { margin: 1rem !important; } .m-lg-4 { margin: 1.5rem !important; } .m-lg-5 { margin: 3rem !important; } .m-lg-auto { margin: auto !important; } .mx-lg-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-lg-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-lg-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-lg-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-lg-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-lg-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-lg-auto { margin-left: auto !important; margin-right: auto !important; } .my-lg-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-lg-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-lg-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-lg-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-lg-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-lg-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-lg-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-lg-0 { margin-top: 0 !important; } .mt-lg-1 { margin-top: 0.25rem !important; } .mt-lg-2 { margin-top: 0.5rem !important; } .mt-lg-3 { margin-top: 1rem !important; } .mt-lg-4 { margin-top: 1.5rem !important; } .mt-lg-5 { margin-top: 3rem !important; } .mt-lg-auto { margin-top: auto !important; } .me-lg-0 { margin-left: 0 !important; } .me-lg-1 { margin-left: 0.25rem !important; } .me-lg-2 { margin-left: 0.5rem !important; } .me-lg-3 { margin-left: 1rem !important; } .me-lg-4 { margin-left: 1.5rem !important; } .me-lg-5 { margin-left: 3rem !important; } .me-lg-auto { margin-left: auto !important; } .mb-lg-0 { margin-bottom: 0 !important; } .mb-lg-1 { margin-bottom: 0.25rem !important; } .mb-lg-2 { margin-bottom: 0.5rem !important; } .mb-lg-3 { margin-bottom: 1rem !important; } .mb-lg-4 { margin-bottom: 1.5rem !important; } .mb-lg-5 { margin-bottom: 3rem !important; } .mb-lg-auto { margin-bottom: auto !important; } .ms-lg-0 { margin-right: 0 !important; } .ms-lg-1 { margin-right: 0.25rem !important; } .ms-lg-2 { margin-right: 0.5rem !important; } .ms-lg-3 { margin-right: 1rem !important; } .ms-lg-4 { margin-right: 1.5rem !important; } .ms-lg-5 { margin-right: 3rem !important; } .ms-lg-auto { margin-right: auto !important; } .p-lg-0 { padding: 0 !important; } .p-lg-1 { padding: 0.25rem !important; } .p-lg-2 { padding: 0.5rem !important; } .p-lg-3 { padding: 1rem !important; } .p-lg-4 { padding: 1.5rem !important; } .p-lg-5 { padding: 3rem !important; } .px-lg-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-lg-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-lg-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-lg-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-lg-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-lg-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-lg-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-lg-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-lg-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-lg-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-lg-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-lg-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-lg-0 { padding-top: 0 !important; } .pt-lg-1 { padding-top: 0.25rem !important; } .pt-lg-2 { padding-top: 0.5rem !important; } .pt-lg-3 { padding-top: 1rem !important; } .pt-lg-4 { padding-top: 1.5rem !important; } .pt-lg-5 { padding-top: 3rem !important; } .pe-lg-0 { padding-left: 0 !important; } .pe-lg-1 { padding-left: 0.25rem !important; } .pe-lg-2 { padding-left: 0.5rem !important; } .pe-lg-3 { padding-left: 1rem !important; } .pe-lg-4 { padding-left: 1.5rem !important; } .pe-lg-5 { padding-left: 3rem !important; } .pb-lg-0 { padding-bottom: 0 !important; } .pb-lg-1 { padding-bottom: 0.25rem !important; } .pb-lg-2 { padding-bottom: 0.5rem !important; } .pb-lg-3 { padding-bottom: 1rem !important; } .pb-lg-4 { padding-bottom: 1.5rem !important; } .pb-lg-5 { padding-bottom: 3rem !important; } .ps-lg-0 { padding-right: 0 !important; } .ps-lg-1 { padding-right: 0.25rem !important; } .ps-lg-2 { padding-right: 0.5rem !important; } .ps-lg-3 { padding-right: 1rem !important; } .ps-lg-4 { padding-right: 1.5rem !important; } .ps-lg-5 { padding-right: 3rem !important; } .text-lg-start { text-align: right !important; } .text-lg-end { text-align: left !important; } .text-lg-center { text-align: center !important; } } @media (min-width: 1200px) { .float-xl-start { float: right !important; } .float-xl-end { float: left !important; } .float-xl-none { float: none !important; } .d-xl-inline { display: inline !important; } .d-xl-inline-block { display: inline-block !important; } .d-xl-block { display: block !important; } .d-xl-grid { display: grid !important; } .d-xl-table { display: table !important; } .d-xl-table-row { display: table-row !important; } .d-xl-table-cell { display: table-cell !important; } .d-xl-flex { display: flex !important; } .d-xl-inline-flex { display: inline-flex !important; } .d-xl-none { display: none !important; } .flex-xl-fill { flex: 1 1 auto !important; } .flex-xl-row { flex-direction: row !important; } .flex-xl-column { flex-direction: column !important; } .flex-xl-row-reverse { flex-direction: row-reverse !important; } .flex-xl-column-reverse { flex-direction: column-reverse !important; } .flex-xl-grow-0 { flex-grow: 0 !important; } .flex-xl-grow-1 { flex-grow: 1 !important; } .flex-xl-shrink-0 { flex-shrink: 0 !important; } .flex-xl-shrink-1 { flex-shrink: 1 !important; } .flex-xl-wrap { flex-wrap: wrap !important; } .flex-xl-nowrap { flex-wrap: nowrap !important; } .flex-xl-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-xl-0 { gap: 0 !important; } .gap-xl-1 { gap: 0.25rem !important; } .gap-xl-2 { gap: 0.5rem !important; } .gap-xl-3 { gap: 1rem !important; } .gap-xl-4 { gap: 1.5rem !important; } .gap-xl-5 { gap: 3rem !important; } .justify-content-xl-start { justify-content: flex-start !important; } .justify-content-xl-end { justify-content: flex-end !important; } .justify-content-xl-center { justify-content: center !important; } .justify-content-xl-between { justify-content: space-between !important; } .justify-content-xl-around { justify-content: space-around !important; } .justify-content-xl-evenly { justify-content: space-evenly !important; } .align-items-xl-start { align-items: flex-start !important; } .align-items-xl-end { align-items: flex-end !important; } .align-items-xl-center { align-items: center !important; } .align-items-xl-baseline { align-items: baseline !important; } .align-items-xl-stretch { align-items: stretch !important; } .align-content-xl-start { align-content: flex-start !important; } .align-content-xl-end { align-content: flex-end !important; } .align-content-xl-center { align-content: center !important; } .align-content-xl-between { align-content: space-between !important; } .align-content-xl-around { align-content: space-around !important; } .align-content-xl-stretch { align-content: stretch !important; } .align-self-xl-auto { align-self: auto !important; } .align-self-xl-start { align-self: flex-start !important; } .align-self-xl-end { align-self: flex-end !important; } .align-self-xl-center { align-self: center !important; } .align-self-xl-baseline { align-self: baseline !important; } .align-self-xl-stretch { align-self: stretch !important; } .order-xl-first { order: -1 !important; } .order-xl-0 { order: 0 !important; } .order-xl-1 { order: 1 !important; } .order-xl-2 { order: 2 !important; } .order-xl-3 { order: 3 !important; } .order-xl-4 { order: 4 !important; } .order-xl-5 { order: 5 !important; } .order-xl-last { order: 6 !important; } .m-xl-0 { margin: 0 !important; } .m-xl-1 { margin: 0.25rem !important; } .m-xl-2 { margin: 0.5rem !important; } .m-xl-3 { margin: 1rem !important; } .m-xl-4 { margin: 1.5rem !important; } .m-xl-5 { margin: 3rem !important; } .m-xl-auto { margin: auto !important; } .mx-xl-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-xl-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-xl-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-xl-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-xl-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-xl-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-xl-auto { margin-left: auto !important; margin-right: auto !important; } .my-xl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xl-0 { margin-top: 0 !important; } .mt-xl-1 { margin-top: 0.25rem !important; } .mt-xl-2 { margin-top: 0.5rem !important; } .mt-xl-3 { margin-top: 1rem !important; } .mt-xl-4 { margin-top: 1.5rem !important; } .mt-xl-5 { margin-top: 3rem !important; } .mt-xl-auto { margin-top: auto !important; } .me-xl-0 { margin-left: 0 !important; } .me-xl-1 { margin-left: 0.25rem !important; } .me-xl-2 { margin-left: 0.5rem !important; } .me-xl-3 { margin-left: 1rem !important; } .me-xl-4 { margin-left: 1.5rem !important; } .me-xl-5 { margin-left: 3rem !important; } .me-xl-auto { margin-left: auto !important; } .mb-xl-0 { margin-bottom: 0 !important; } .mb-xl-1 { margin-bottom: 0.25rem !important; } .mb-xl-2 { margin-bottom: 0.5rem !important; } .mb-xl-3 { margin-bottom: 1rem !important; } .mb-xl-4 { margin-bottom: 1.5rem !important; } .mb-xl-5 { margin-bottom: 3rem !important; } .mb-xl-auto { margin-bottom: auto !important; } .ms-xl-0 { margin-right: 0 !important; } .ms-xl-1 { margin-right: 0.25rem !important; } .ms-xl-2 { margin-right: 0.5rem !important; } .ms-xl-3 { margin-right: 1rem !important; } .ms-xl-4 { margin-right: 1.5rem !important; } .ms-xl-5 { margin-right: 3rem !important; } .ms-xl-auto { margin-right: auto !important; } .p-xl-0 { padding: 0 !important; } .p-xl-1 { padding: 0.25rem !important; } .p-xl-2 { padding: 0.5rem !important; } .p-xl-3 { padding: 1rem !important; } .p-xl-4 { padding: 1.5rem !important; } .p-xl-5 { padding: 3rem !important; } .px-xl-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-xl-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-xl-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-xl-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-xl-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-xl-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-xl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xl-0 { padding-top: 0 !important; } .pt-xl-1 { padding-top: 0.25rem !important; } .pt-xl-2 { padding-top: 0.5rem !important; } .pt-xl-3 { padding-top: 1rem !important; } .pt-xl-4 { padding-top: 1.5rem !important; } .pt-xl-5 { padding-top: 3rem !important; } .pe-xl-0 { padding-left: 0 !important; } .pe-xl-1 { padding-left: 0.25rem !important; } .pe-xl-2 { padding-left: 0.5rem !important; } .pe-xl-3 { padding-left: 1rem !important; } .pe-xl-4 { padding-left: 1.5rem !important; } .pe-xl-5 { padding-left: 3rem !important; } .pb-xl-0 { padding-bottom: 0 !important; } .pb-xl-1 { padding-bottom: 0.25rem !important; } .pb-xl-2 { padding-bottom: 0.5rem !important; } .pb-xl-3 { padding-bottom: 1rem !important; } .pb-xl-4 { padding-bottom: 1.5rem !important; } .pb-xl-5 { padding-bottom: 3rem !important; } .ps-xl-0 { padding-right: 0 !important; } .ps-xl-1 { padding-right: 0.25rem !important; } .ps-xl-2 { padding-right: 0.5rem !important; } .ps-xl-3 { padding-right: 1rem !important; } .ps-xl-4 { padding-right: 1.5rem !important; } .ps-xl-5 { padding-right: 3rem !important; } .text-xl-start { text-align: right !important; } .text-xl-end { text-align: left !important; } .text-xl-center { text-align: center !important; } } @media (min-width: 1400px) { .float-xxl-start { float: right !important; } .float-xxl-end { float: left !important; } .float-xxl-none { float: none !important; } .d-xxl-inline { display: inline !important; } .d-xxl-inline-block { display: inline-block !important; } .d-xxl-block { display: block !important; } .d-xxl-grid { display: grid !important; } .d-xxl-table { display: table !important; } .d-xxl-table-row { display: table-row !important; } .d-xxl-table-cell { display: table-cell !important; } .d-xxl-flex { display: flex !important; } .d-xxl-inline-flex { display: inline-flex !important; } .d-xxl-none { display: none !important; } .flex-xxl-fill { flex: 1 1 auto !important; } .flex-xxl-row { flex-direction: row !important; } .flex-xxl-column { flex-direction: column !important; } .flex-xxl-row-reverse { flex-direction: row-reverse !important; } .flex-xxl-column-reverse { flex-direction: column-reverse !important; } .flex-xxl-grow-0 { flex-grow: 0 !important; } .flex-xxl-grow-1 { flex-grow: 1 !important; } .flex-xxl-shrink-0 { flex-shrink: 0 !important; } .flex-xxl-shrink-1 { flex-shrink: 1 !important; } .flex-xxl-wrap { flex-wrap: wrap !important; } .flex-xxl-nowrap { flex-wrap: nowrap !important; } .flex-xxl-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-xxl-0 { gap: 0 !important; } .gap-xxl-1 { gap: 0.25rem !important; } .gap-xxl-2 { gap: 0.5rem !important; } .gap-xxl-3 { gap: 1rem !important; } .gap-xxl-4 { gap: 1.5rem !important; } .gap-xxl-5 { gap: 3rem !important; } .justify-content-xxl-start { justify-content: flex-start !important; } .justify-content-xxl-end { justify-content: flex-end !important; } .justify-content-xxl-center { justify-content: center !important; } .justify-content-xxl-between { justify-content: space-between !important; } .justify-content-xxl-around { justify-content: space-around !important; } .justify-content-xxl-evenly { justify-content: space-evenly !important; } .align-items-xxl-start { align-items: flex-start !important; } .align-items-xxl-end { align-items: flex-end !important; } .align-items-xxl-center { align-items: center !important; } .align-items-xxl-baseline { align-items: baseline !important; } .align-items-xxl-stretch { align-items: stretch !important; } .align-content-xxl-start { align-content: flex-start !important; } .align-content-xxl-end { align-content: flex-end !important; } .align-content-xxl-center { align-content: center !important; } .align-content-xxl-between { align-content: space-between !important; } .align-content-xxl-around { align-content: space-around !important; } .align-content-xxl-stretch { align-content: stretch !important; } .align-self-xxl-auto { align-self: auto !important; } .align-self-xxl-start { align-self: flex-start !important; } .align-self-xxl-end { align-self: flex-end !important; } .align-self-xxl-center { align-self: center !important; } .align-self-xxl-baseline { align-self: baseline !important; } .align-self-xxl-stretch { align-self: stretch !important; } .order-xxl-first { order: -1 !important; } .order-xxl-0 { order: 0 !important; } .order-xxl-1 { order: 1 !important; } .order-xxl-2 { order: 2 !important; } .order-xxl-3 { order: 3 !important; } .order-xxl-4 { order: 4 !important; } .order-xxl-5 { order: 5 !important; } .order-xxl-last { order: 6 !important; } .m-xxl-0 { margin: 0 !important; } .m-xxl-1 { margin: 0.25rem !important; } .m-xxl-2 { margin: 0.5rem !important; } .m-xxl-3 { margin: 1rem !important; } .m-xxl-4 { margin: 1.5rem !important; } .m-xxl-5 { margin: 3rem !important; } .m-xxl-auto { margin: auto !important; } .mx-xxl-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-xxl-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-xxl-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-xxl-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-xxl-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-xxl-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-xxl-auto { margin-left: auto !important; margin-right: auto !important; } .my-xxl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xxl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xxl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xxl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xxl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xxl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xxl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xxl-0 { margin-top: 0 !important; } .mt-xxl-1 { margin-top: 0.25rem !important; } .mt-xxl-2 { margin-top: 0.5rem !important; } .mt-xxl-3 { margin-top: 1rem !important; } .mt-xxl-4 { margin-top: 1.5rem !important; } .mt-xxl-5 { margin-top: 3rem !important; } .mt-xxl-auto { margin-top: auto !important; } .me-xxl-0 { margin-left: 0 !important; } .me-xxl-1 { margin-left: 0.25rem !important; } .me-xxl-2 { margin-left: 0.5rem !important; } .me-xxl-3 { margin-left: 1rem !important; } .me-xxl-4 { margin-left: 1.5rem !important; } .me-xxl-5 { margin-left: 3rem !important; } .me-xxl-auto { margin-left: auto !important; } .mb-xxl-0 { margin-bottom: 0 !important; } .mb-xxl-1 { margin-bottom: 0.25rem !important; } .mb-xxl-2 { margin-bottom: 0.5rem !important; } .mb-xxl-3 { margin-bottom: 1rem !important; } .mb-xxl-4 { margin-bottom: 1.5rem !important; } .mb-xxl-5 { margin-bottom: 3rem !important; } .mb-xxl-auto { margin-bottom: auto !important; } .ms-xxl-0 { margin-right: 0 !important; } .ms-xxl-1 { margin-right: 0.25rem !important; } .ms-xxl-2 { margin-right: 0.5rem !important; } .ms-xxl-3 { margin-right: 1rem !important; } .ms-xxl-4 { margin-right: 1.5rem !important; } .ms-xxl-5 { margin-right: 3rem !important; } .ms-xxl-auto { margin-right: auto !important; } .p-xxl-0 { padding: 0 !important; } .p-xxl-1 { padding: 0.25rem !important; } .p-xxl-2 { padding: 0.5rem !important; } .p-xxl-3 { padding: 1rem !important; } .p-xxl-4 { padding: 1.5rem !important; } .p-xxl-5 { padding: 3rem !important; } .px-xxl-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-xxl-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-xxl-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-xxl-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-xxl-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-xxl-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-xxl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xxl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xxl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xxl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xxl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xxl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xxl-0 { padding-top: 0 !important; } .pt-xxl-1 { padding-top: 0.25rem !important; } .pt-xxl-2 { padding-top: 0.5rem !important; } .pt-xxl-3 { padding-top: 1rem !important; } .pt-xxl-4 { padding-top: 1.5rem !important; } .pt-xxl-5 { padding-top: 3rem !important; } .pe-xxl-0 { padding-left: 0 !important; } .pe-xxl-1 { padding-left: 0.25rem !important; } .pe-xxl-2 { padding-left: 0.5rem !important; } .pe-xxl-3 { padding-left: 1rem !important; } .pe-xxl-4 { padding-left: 1.5rem !important; } .pe-xxl-5 { padding-left: 3rem !important; } .pb-xxl-0 { padding-bottom: 0 !important; } .pb-xxl-1 { padding-bottom: 0.25rem !important; } .pb-xxl-2 { padding-bottom: 0.5rem !important; } .pb-xxl-3 { padding-bottom: 1rem !important; } .pb-xxl-4 { padding-bottom: 1.5rem !important; } .pb-xxl-5 { padding-bottom: 3rem !important; } .ps-xxl-0 { padding-right: 0 !important; } .ps-xxl-1 { padding-right: 0.25rem !important; } .ps-xxl-2 { padding-right: 0.5rem !important; } .ps-xxl-3 { padding-right: 1rem !important; } .ps-xxl-4 { padding-right: 1.5rem !important; } .ps-xxl-5 { padding-right: 3rem !important; } .text-xxl-start { text-align: right !important; } .text-xxl-end { text-align: left !important; } .text-xxl-center { text-align: center !important; } } @media (min-width: 1200px) { .fs-1 { font-size: 2.5rem !important; } .fs-2 { font-size: 2rem !important; } .fs-3 { font-size: 1.75rem !important; } .fs-4 { font-size: 1.5rem !important; } } @media print { .d-print-inline { display: inline !important; } .d-print-inline-block { display: inline-block !important; } .d-print-block { display: block !important; } .d-print-grid { display: grid !important; } .d-print-table { display: table !important; } .d-print-table-row { display: table-row !important; } .d-print-table-cell { display: table-cell !important; } .d-print-flex { display: flex !important; } .d-print-inline-flex { display: inline-flex !important; } .d-print-none { display: none !important; } } /*# sourceMappingURL=bootstrap-utilities.rtl.css.map */ ================================================ FILE: examples/tailwind-and-bootstrap-example/css-modules/bootstrap/bootstrap.css ================================================ @charset "UTF-8"; /*! * Bootstrap v5.1.3 (https://getbootstrap.com/) * Copyright 2011-2021 The Bootstrap Authors * Copyright 2011-2021 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ :root { --bs-blue: #0d6efd; --bs-indigo: #6610f2; --bs-purple: #6f42c1; --bs-pink: #d63384; --bs-red: #dc3545; --bs-orange: #fd7e14; --bs-yellow: #ffc107; --bs-green: #198754; --bs-teal: #20c997; --bs-cyan: #0dcaf0; --bs-white: #fff; --bs-gray: #6c757d; --bs-gray-dark: #343a40; --bs-gray-100: #f8f9fa; --bs-gray-200: #e9ecef; --bs-gray-300: #dee2e6; --bs-gray-400: #ced4da; --bs-gray-500: #adb5bd; --bs-gray-600: #6c757d; --bs-gray-700: #495057; --bs-gray-800: #343a40; --bs-gray-900: #212529; --bs-primary: #0d6efd; --bs-secondary: #6c757d; --bs-success: #198754; --bs-info: #0dcaf0; --bs-warning: #ffc107; --bs-danger: #dc3545; --bs-light: #f8f9fa; --bs-dark: #212529; --bs-primary-rgb: 13, 110, 253; --bs-secondary-rgb: 108, 117, 125; --bs-success-rgb: 25, 135, 84; --bs-info-rgb: 13, 202, 240; --bs-warning-rgb: 255, 193, 7; --bs-danger-rgb: 220, 53, 69; --bs-light-rgb: 248, 249, 250; --bs-dark-rgb: 33, 37, 41; --bs-white-rgb: 255, 255, 255; --bs-black-rgb: 0, 0, 0; --bs-body-color-rgb: 33, 37, 41; --bs-body-bg-rgb: 255, 255, 255; --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --bs-gradient: linear-gradient( 180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0) ); --bs-body-font-family: var(--bs-font-sans-serif); --bs-body-font-size: 1rem; --bs-body-font-weight: 400; --bs-body-line-height: 1.5; --bs-body-color: #212529; --bs-body-bg: #fff; } *, *::before, *::after { box-sizing: border-box; } @media (prefers-reduced-motion: no-preference) { :root { scroll-behavior: smooth; } } body { margin: 0; font-family: var(--bs-body-font-family); font-size: var(--bs-body-font-size); font-weight: var(--bs-body-font-weight); line-height: var(--bs-body-line-height); color: var(--bs-body-color); text-align: var(--bs-body-text-align); background-color: var(--bs-body-bg); -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } hr { margin: 1rem 0; color: inherit; background-color: currentColor; border: 0; opacity: 0.25; } hr:not([size]) { height: 1px; } h6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 { margin-top: 0; margin-bottom: 0.5rem; font-weight: 500; line-height: 1.2; } h1, .h1 { font-size: calc(1.375rem + 1.5vw); } @media (min-width: 1200px) { h1, .h1 { font-size: 2.5rem; } } h2, .h2 { font-size: calc(1.325rem + 0.9vw); } @media (min-width: 1200px) { h2, .h2 { font-size: 2rem; } } h3, .h3 { font-size: calc(1.3rem + 0.6vw); } @media (min-width: 1200px) { h3, .h3 { font-size: 1.75rem; } } h4, .h4 { font-size: calc(1.275rem + 0.3vw); } @media (min-width: 1200px) { h4, .h4 { font-size: 1.5rem; } } h5, .h5 { font-size: 1.25rem; } h6, .h6 { font-size: 1rem; } p { margin-top: 0; margin-bottom: 1rem; } abbr[title], abbr[data-bs-original-title] { -webkit-text-decoration: underline dotted; text-decoration: underline dotted; cursor: help; -webkit-text-decoration-skip-ink: none; text-decoration-skip-ink: none; } address { margin-bottom: 1rem; font-style: normal; line-height: inherit; } ol, ul { padding-left: 2rem; } ol, ul, dl { margin-top: 0; margin-bottom: 1rem; } ol ol, ul ul, ol ul, ul ol { margin-bottom: 0; } dt { font-weight: 700; } dd { margin-bottom: 0.5rem; margin-left: 0; } blockquote { margin: 0 0 1rem; } b, strong { font-weight: bolder; } small, .small { font-size: 0.875em; } mark, .mark { padding: 0.2em; background-color: #fcf8e3; } sub, sup { position: relative; font-size: 0.75em; line-height: 0; vertical-align: baseline; } sub { bottom: -0.25em; } sup { top: -0.5em; } a { color: #0d6efd; text-decoration: underline; } a:hover { color: #0a58ca; } a:not([href]):not([class]), a:not([href]):not([class]):hover { color: inherit; text-decoration: none; } pre, code, kbd, samp { font-family: var(--bs-font-monospace); font-size: 1em; direction: ltr /* rtl:ignore */; unicode-bidi: bidi-override; } pre { display: block; margin-top: 0; margin-bottom: 1rem; overflow: auto; font-size: 0.875em; } pre code { font-size: inherit; color: inherit; word-break: normal; } code { font-size: 0.875em; color: #d63384; word-wrap: break-word; } a > code { color: inherit; } kbd { padding: 0.2rem 0.4rem; font-size: 0.875em; color: #fff; background-color: #212529; border-radius: 0.2rem; } kbd kbd { padding: 0; font-size: 1em; font-weight: 700; } figure { margin: 0 0 1rem; } img, svg { vertical-align: middle; } table { caption-side: bottom; border-collapse: collapse; } caption { padding-top: 0.5rem; padding-bottom: 0.5rem; color: #6c757d; text-align: left; } th { text-align: inherit; text-align: -webkit-match-parent; } thead, tbody, tfoot, tr, td, th { border-color: inherit; border-style: solid; border-width: 0; } label { display: inline-block; } button { border-radius: 0; } button:focus:not(:focus-visible) { outline: 0; } input, button, select, optgroup, textarea { margin: 0; font-family: inherit; font-size: inherit; line-height: inherit; } button, select { text-transform: none; } [role="button"] { cursor: pointer; } select { word-wrap: normal; } select:disabled { opacity: 1; } [list]::-webkit-calendar-picker-indicator { display: none; } button, [type="button"], [type="reset"], [type="submit"] { -webkit-appearance: button; } button:not(:disabled), [type="button"]:not(:disabled), [type="reset"]:not(:disabled), [type="submit"]:not(:disabled) { cursor: pointer; } ::-moz-focus-inner { padding: 0; border-style: none; } textarea { resize: vertical; } fieldset { min-width: 0; padding: 0; margin: 0; border: 0; } legend { float: left; width: 100%; padding: 0; margin-bottom: 0.5rem; font-size: calc(1.275rem + 0.3vw); line-height: inherit; } @media (min-width: 1200px) { legend { font-size: 1.5rem; } } legend + * { clear: left; } ::-webkit-datetime-edit-fields-wrapper, ::-webkit-datetime-edit-text, ::-webkit-datetime-edit-minute, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-year-field { padding: 0; } ::-webkit-inner-spin-button { height: auto; } [type="search"] { outline-offset: -2px; -webkit-appearance: textfield; } /* rtl:raw: [type="tel"], [type="url"], [type="email"], [type="number"] { direction: ltr; } */ ::-webkit-search-decoration { -webkit-appearance: none; } ::-webkit-color-swatch-wrapper { padding: 0; } ::-webkit-file-upload-button { font: inherit; } ::file-selector-button { font: inherit; } ::-webkit-file-upload-button { font: inherit; -webkit-appearance: button; } output { display: inline-block; } iframe { border: 0; } summary { display: list-item; cursor: pointer; } progress { vertical-align: baseline; } [hidden] { display: none !important; } .lead { font-size: 1.25rem; font-weight: 300; } .display-1 { font-size: calc(1.625rem + 4.5vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-1 { font-size: 5rem; } } .display-2 { font-size: calc(1.575rem + 3.9vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-2 { font-size: 4.5rem; } } .display-3 { font-size: calc(1.525rem + 3.3vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-3 { font-size: 4rem; } } .display-4 { font-size: calc(1.475rem + 2.7vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-4 { font-size: 3.5rem; } } .display-5 { font-size: calc(1.425rem + 2.1vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-5 { font-size: 3rem; } } .display-6 { font-size: calc(1.375rem + 1.5vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-6 { font-size: 2.5rem; } } .list-unstyled { padding-left: 0; list-style: none; } .list-inline { padding-left: 0; list-style: none; } .list-inline-item { display: inline-block; } .list-inline-item:not(:last-child) { margin-right: 0.5rem; } .initialism { font-size: 0.875em; text-transform: uppercase; } .blockquote { margin-bottom: 1rem; font-size: 1.25rem; } .blockquote > :last-child { margin-bottom: 0; } .blockquote-footer { margin-top: -1rem; margin-bottom: 1rem; font-size: 0.875em; color: #6c757d; } .blockquote-footer::before { content: "— "; } .img-fluid { max-width: 100%; height: auto; } .img-thumbnail { padding: 0.25rem; background-color: #fff; border: 1px solid #dee2e6; border-radius: 0.25rem; max-width: 100%; height: auto; } .figure { display: inline-block; } .figure-img { margin-bottom: 0.5rem; line-height: 1; } .figure-caption { font-size: 0.875em; color: #6c757d; } .container, .container-fluid, .container-xxl, .container-xl, .container-lg, .container-md, .container-sm { width: 100%; padding-right: var(--bs-gutter-x, 0.75rem); padding-left: var(--bs-gutter-x, 0.75rem); margin-right: auto; margin-left: auto; } @media (min-width: 576px) { .container-sm, .container { max-width: 540px; } } @media (min-width: 768px) { .container-md, .container-sm, .container { max-width: 720px; } } @media (min-width: 992px) { .container-lg, .container-md, .container-sm, .container { max-width: 960px; } } @media (min-width: 1200px) { .container-xl, .container-lg, .container-md, .container-sm, .container { max-width: 1140px; } } @media (min-width: 1400px) { .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { max-width: 1320px; } } .row { --bs-gutter-x: 1.5rem; --bs-gutter-y: 0; display: flex; flex-wrap: wrap; margin-top: calc(-1 * var(--bs-gutter-y)); margin-right: calc(-0.5 * var(--bs-gutter-x)); margin-left: calc(-0.5 * var(--bs-gutter-x)); } .row > * { flex-shrink: 0; width: 100%; max-width: 100%; padding-right: calc(var(--bs-gutter-x) * 0.5); padding-left: calc(var(--bs-gutter-x) * 0.5); margin-top: var(--bs-gutter-y); } .col { flex: 1 0 0%; } .row-cols-auto > * { flex: 0 0 auto; width: auto; } .row-cols-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-auto { flex: 0 0 auto; width: auto; } .col-1 { flex: 0 0 auto; width: 8.33333333%; } .col-2 { flex: 0 0 auto; width: 16.66666667%; } .col-3 { flex: 0 0 auto; width: 25%; } .col-4 { flex: 0 0 auto; width: 33.33333333%; } .col-5 { flex: 0 0 auto; width: 41.66666667%; } .col-6 { flex: 0 0 auto; width: 50%; } .col-7 { flex: 0 0 auto; width: 58.33333333%; } .col-8 { flex: 0 0 auto; width: 66.66666667%; } .col-9 { flex: 0 0 auto; width: 75%; } .col-10 { flex: 0 0 auto; width: 83.33333333%; } .col-11 { flex: 0 0 auto; width: 91.66666667%; } .col-12 { flex: 0 0 auto; width: 100%; } .offset-1 { margin-left: 8.33333333%; } .offset-2 { margin-left: 16.66666667%; } .offset-3 { margin-left: 25%; } .offset-4 { margin-left: 33.33333333%; } .offset-5 { margin-left: 41.66666667%; } .offset-6 { margin-left: 50%; } .offset-7 { margin-left: 58.33333333%; } .offset-8 { margin-left: 66.66666667%; } .offset-9 { margin-left: 75%; } .offset-10 { margin-left: 83.33333333%; } .offset-11 { margin-left: 91.66666667%; } .g-0, .gx-0 { --bs-gutter-x: 0; } .g-0, .gy-0 { --bs-gutter-y: 0; } .g-1, .gx-1 { --bs-gutter-x: 0.25rem; } .g-1, .gy-1 { --bs-gutter-y: 0.25rem; } .g-2, .gx-2 { --bs-gutter-x: 0.5rem; } .g-2, .gy-2 { --bs-gutter-y: 0.5rem; } .g-3, .gx-3 { --bs-gutter-x: 1rem; } .g-3, .gy-3 { --bs-gutter-y: 1rem; } .g-4, .gx-4 { --bs-gutter-x: 1.5rem; } .g-4, .gy-4 { --bs-gutter-y: 1.5rem; } .g-5, .gx-5 { --bs-gutter-x: 3rem; } .g-5, .gy-5 { --bs-gutter-y: 3rem; } @media (min-width: 576px) { .col-sm { flex: 1 0 0%; } .row-cols-sm-auto > * { flex: 0 0 auto; width: auto; } .row-cols-sm-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-sm-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-sm-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-sm-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-sm-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-sm-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-sm-auto { flex: 0 0 auto; width: auto; } .col-sm-1 { flex: 0 0 auto; width: 8.33333333%; } .col-sm-2 { flex: 0 0 auto; width: 16.66666667%; } .col-sm-3 { flex: 0 0 auto; width: 25%; } .col-sm-4 { flex: 0 0 auto; width: 33.33333333%; } .col-sm-5 { flex: 0 0 auto; width: 41.66666667%; } .col-sm-6 { flex: 0 0 auto; width: 50%; } .col-sm-7 { flex: 0 0 auto; width: 58.33333333%; } .col-sm-8 { flex: 0 0 auto; width: 66.66666667%; } .col-sm-9 { flex: 0 0 auto; width: 75%; } .col-sm-10 { flex: 0 0 auto; width: 83.33333333%; } .col-sm-11 { flex: 0 0 auto; width: 91.66666667%; } .col-sm-12 { flex: 0 0 auto; width: 100%; } .offset-sm-0 { margin-left: 0; } .offset-sm-1 { margin-left: 8.33333333%; } .offset-sm-2 { margin-left: 16.66666667%; } .offset-sm-3 { margin-left: 25%; } .offset-sm-4 { margin-left: 33.33333333%; } .offset-sm-5 { margin-left: 41.66666667%; } .offset-sm-6 { margin-left: 50%; } .offset-sm-7 { margin-left: 58.33333333%; } .offset-sm-8 { margin-left: 66.66666667%; } .offset-sm-9 { margin-left: 75%; } .offset-sm-10 { margin-left: 83.33333333%; } .offset-sm-11 { margin-left: 91.66666667%; } .g-sm-0, .gx-sm-0 { --bs-gutter-x: 0; } .g-sm-0, .gy-sm-0 { --bs-gutter-y: 0; } .g-sm-1, .gx-sm-1 { --bs-gutter-x: 0.25rem; } .g-sm-1, .gy-sm-1 { --bs-gutter-y: 0.25rem; } .g-sm-2, .gx-sm-2 { --bs-gutter-x: 0.5rem; } .g-sm-2, .gy-sm-2 { --bs-gutter-y: 0.5rem; } .g-sm-3, .gx-sm-3 { --bs-gutter-x: 1rem; } .g-sm-3, .gy-sm-3 { --bs-gutter-y: 1rem; } .g-sm-4, .gx-sm-4 { --bs-gutter-x: 1.5rem; } .g-sm-4, .gy-sm-4 { --bs-gutter-y: 1.5rem; } .g-sm-5, .gx-sm-5 { --bs-gutter-x: 3rem; } .g-sm-5, .gy-sm-5 { --bs-gutter-y: 3rem; } } @media (min-width: 768px) { .col-md { flex: 1 0 0%; } .row-cols-md-auto > * { flex: 0 0 auto; width: auto; } .row-cols-md-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-md-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-md-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-md-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-md-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-md-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-md-auto { flex: 0 0 auto; width: auto; } .col-md-1 { flex: 0 0 auto; width: 8.33333333%; } .col-md-2 { flex: 0 0 auto; width: 16.66666667%; } .col-md-3 { flex: 0 0 auto; width: 25%; } .col-md-4 { flex: 0 0 auto; width: 33.33333333%; } .col-md-5 { flex: 0 0 auto; width: 41.66666667%; } .col-md-6 { flex: 0 0 auto; width: 50%; } .col-md-7 { flex: 0 0 auto; width: 58.33333333%; } .col-md-8 { flex: 0 0 auto; width: 66.66666667%; } .col-md-9 { flex: 0 0 auto; width: 75%; } .col-md-10 { flex: 0 0 auto; width: 83.33333333%; } .col-md-11 { flex: 0 0 auto; width: 91.66666667%; } .col-md-12 { flex: 0 0 auto; width: 100%; } .offset-md-0 { margin-left: 0; } .offset-md-1 { margin-left: 8.33333333%; } .offset-md-2 { margin-left: 16.66666667%; } .offset-md-3 { margin-left: 25%; } .offset-md-4 { margin-left: 33.33333333%; } .offset-md-5 { margin-left: 41.66666667%; } .offset-md-6 { margin-left: 50%; } .offset-md-7 { margin-left: 58.33333333%; } .offset-md-8 { margin-left: 66.66666667%; } .offset-md-9 { margin-left: 75%; } .offset-md-10 { margin-left: 83.33333333%; } .offset-md-11 { margin-left: 91.66666667%; } .g-md-0, .gx-md-0 { --bs-gutter-x: 0; } .g-md-0, .gy-md-0 { --bs-gutter-y: 0; } .g-md-1, .gx-md-1 { --bs-gutter-x: 0.25rem; } .g-md-1, .gy-md-1 { --bs-gutter-y: 0.25rem; } .g-md-2, .gx-md-2 { --bs-gutter-x: 0.5rem; } .g-md-2, .gy-md-2 { --bs-gutter-y: 0.5rem; } .g-md-3, .gx-md-3 { --bs-gutter-x: 1rem; } .g-md-3, .gy-md-3 { --bs-gutter-y: 1rem; } .g-md-4, .gx-md-4 { --bs-gutter-x: 1.5rem; } .g-md-4, .gy-md-4 { --bs-gutter-y: 1.5rem; } .g-md-5, .gx-md-5 { --bs-gutter-x: 3rem; } .g-md-5, .gy-md-5 { --bs-gutter-y: 3rem; } } @media (min-width: 992px) { .col-lg { flex: 1 0 0%; } .row-cols-lg-auto > * { flex: 0 0 auto; width: auto; } .row-cols-lg-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-lg-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-lg-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-lg-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-lg-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-lg-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-lg-auto { flex: 0 0 auto; width: auto; } .col-lg-1 { flex: 0 0 auto; width: 8.33333333%; } .col-lg-2 { flex: 0 0 auto; width: 16.66666667%; } .col-lg-3 { flex: 0 0 auto; width: 25%; } .col-lg-4 { flex: 0 0 auto; width: 33.33333333%; } .col-lg-5 { flex: 0 0 auto; width: 41.66666667%; } .col-lg-6 { flex: 0 0 auto; width: 50%; } .col-lg-7 { flex: 0 0 auto; width: 58.33333333%; } .col-lg-8 { flex: 0 0 auto; width: 66.66666667%; } .col-lg-9 { flex: 0 0 auto; width: 75%; } .col-lg-10 { flex: 0 0 auto; width: 83.33333333%; } .col-lg-11 { flex: 0 0 auto; width: 91.66666667%; } .col-lg-12 { flex: 0 0 auto; width: 100%; } .offset-lg-0 { margin-left: 0; } .offset-lg-1 { margin-left: 8.33333333%; } .offset-lg-2 { margin-left: 16.66666667%; } .offset-lg-3 { margin-left: 25%; } .offset-lg-4 { margin-left: 33.33333333%; } .offset-lg-5 { margin-left: 41.66666667%; } .offset-lg-6 { margin-left: 50%; } .offset-lg-7 { margin-left: 58.33333333%; } .offset-lg-8 { margin-left: 66.66666667%; } .offset-lg-9 { margin-left: 75%; } .offset-lg-10 { margin-left: 83.33333333%; } .offset-lg-11 { margin-left: 91.66666667%; } .g-lg-0, .gx-lg-0 { --bs-gutter-x: 0; } .g-lg-0, .gy-lg-0 { --bs-gutter-y: 0; } .g-lg-1, .gx-lg-1 { --bs-gutter-x: 0.25rem; } .g-lg-1, .gy-lg-1 { --bs-gutter-y: 0.25rem; } .g-lg-2, .gx-lg-2 { --bs-gutter-x: 0.5rem; } .g-lg-2, .gy-lg-2 { --bs-gutter-y: 0.5rem; } .g-lg-3, .gx-lg-3 { --bs-gutter-x: 1rem; } .g-lg-3, .gy-lg-3 { --bs-gutter-y: 1rem; } .g-lg-4, .gx-lg-4 { --bs-gutter-x: 1.5rem; } .g-lg-4, .gy-lg-4 { --bs-gutter-y: 1.5rem; } .g-lg-5, .gx-lg-5 { --bs-gutter-x: 3rem; } .g-lg-5, .gy-lg-5 { --bs-gutter-y: 3rem; } } @media (min-width: 1200px) { .col-xl { flex: 1 0 0%; } .row-cols-xl-auto > * { flex: 0 0 auto; width: auto; } .row-cols-xl-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-xl-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-xl-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-xl-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-xl-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-xl-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-xl-auto { flex: 0 0 auto; width: auto; } .col-xl-1 { flex: 0 0 auto; width: 8.33333333%; } .col-xl-2 { flex: 0 0 auto; width: 16.66666667%; } .col-xl-3 { flex: 0 0 auto; width: 25%; } .col-xl-4 { flex: 0 0 auto; width: 33.33333333%; } .col-xl-5 { flex: 0 0 auto; width: 41.66666667%; } .col-xl-6 { flex: 0 0 auto; width: 50%; } .col-xl-7 { flex: 0 0 auto; width: 58.33333333%; } .col-xl-8 { flex: 0 0 auto; width: 66.66666667%; } .col-xl-9 { flex: 0 0 auto; width: 75%; } .col-xl-10 { flex: 0 0 auto; width: 83.33333333%; } .col-xl-11 { flex: 0 0 auto; width: 91.66666667%; } .col-xl-12 { flex: 0 0 auto; width: 100%; } .offset-xl-0 { margin-left: 0; } .offset-xl-1 { margin-left: 8.33333333%; } .offset-xl-2 { margin-left: 16.66666667%; } .offset-xl-3 { margin-left: 25%; } .offset-xl-4 { margin-left: 33.33333333%; } .offset-xl-5 { margin-left: 41.66666667%; } .offset-xl-6 { margin-left: 50%; } .offset-xl-7 { margin-left: 58.33333333%; } .offset-xl-8 { margin-left: 66.66666667%; } .offset-xl-9 { margin-left: 75%; } .offset-xl-10 { margin-left: 83.33333333%; } .offset-xl-11 { margin-left: 91.66666667%; } .g-xl-0, .gx-xl-0 { --bs-gutter-x: 0; } .g-xl-0, .gy-xl-0 { --bs-gutter-y: 0; } .g-xl-1, .gx-xl-1 { --bs-gutter-x: 0.25rem; } .g-xl-1, .gy-xl-1 { --bs-gutter-y: 0.25rem; } .g-xl-2, .gx-xl-2 { --bs-gutter-x: 0.5rem; } .g-xl-2, .gy-xl-2 { --bs-gutter-y: 0.5rem; } .g-xl-3, .gx-xl-3 { --bs-gutter-x: 1rem; } .g-xl-3, .gy-xl-3 { --bs-gutter-y: 1rem; } .g-xl-4, .gx-xl-4 { --bs-gutter-x: 1.5rem; } .g-xl-4, .gy-xl-4 { --bs-gutter-y: 1.5rem; } .g-xl-5, .gx-xl-5 { --bs-gutter-x: 3rem; } .g-xl-5, .gy-xl-5 { --bs-gutter-y: 3rem; } } @media (min-width: 1400px) { .col-xxl { flex: 1 0 0%; } .row-cols-xxl-auto > * { flex: 0 0 auto; width: auto; } .row-cols-xxl-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-xxl-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-xxl-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-xxl-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-xxl-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-xxl-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-xxl-auto { flex: 0 0 auto; width: auto; } .col-xxl-1 { flex: 0 0 auto; width: 8.33333333%; } .col-xxl-2 { flex: 0 0 auto; width: 16.66666667%; } .col-xxl-3 { flex: 0 0 auto; width: 25%; } .col-xxl-4 { flex: 0 0 auto; width: 33.33333333%; } .col-xxl-5 { flex: 0 0 auto; width: 41.66666667%; } .col-xxl-6 { flex: 0 0 auto; width: 50%; } .col-xxl-7 { flex: 0 0 auto; width: 58.33333333%; } .col-xxl-8 { flex: 0 0 auto; width: 66.66666667%; } .col-xxl-9 { flex: 0 0 auto; width: 75%; } .col-xxl-10 { flex: 0 0 auto; width: 83.33333333%; } .col-xxl-11 { flex: 0 0 auto; width: 91.66666667%; } .col-xxl-12 { flex: 0 0 auto; width: 100%; } .offset-xxl-0 { margin-left: 0; } .offset-xxl-1 { margin-left: 8.33333333%; } .offset-xxl-2 { margin-left: 16.66666667%; } .offset-xxl-3 { margin-left: 25%; } .offset-xxl-4 { margin-left: 33.33333333%; } .offset-xxl-5 { margin-left: 41.66666667%; } .offset-xxl-6 { margin-left: 50%; } .offset-xxl-7 { margin-left: 58.33333333%; } .offset-xxl-8 { margin-left: 66.66666667%; } .offset-xxl-9 { margin-left: 75%; } .offset-xxl-10 { margin-left: 83.33333333%; } .offset-xxl-11 { margin-left: 91.66666667%; } .g-xxl-0, .gx-xxl-0 { --bs-gutter-x: 0; } .g-xxl-0, .gy-xxl-0 { --bs-gutter-y: 0; } .g-xxl-1, .gx-xxl-1 { --bs-gutter-x: 0.25rem; } .g-xxl-1, .gy-xxl-1 { --bs-gutter-y: 0.25rem; } .g-xxl-2, .gx-xxl-2 { --bs-gutter-x: 0.5rem; } .g-xxl-2, .gy-xxl-2 { --bs-gutter-y: 0.5rem; } .g-xxl-3, .gx-xxl-3 { --bs-gutter-x: 1rem; } .g-xxl-3, .gy-xxl-3 { --bs-gutter-y: 1rem; } .g-xxl-4, .gx-xxl-4 { --bs-gutter-x: 1.5rem; } .g-xxl-4, .gy-xxl-4 { --bs-gutter-y: 1.5rem; } .g-xxl-5, .gx-xxl-5 { --bs-gutter-x: 3rem; } .g-xxl-5, .gy-xxl-5 { --bs-gutter-y: 3rem; } } .table { --bs-table-bg: transparent; --bs-table-accent-bg: transparent; --bs-table-striped-color: #212529; --bs-table-striped-bg: rgba(0, 0, 0, 0.05); --bs-table-active-color: #212529; --bs-table-active-bg: rgba(0, 0, 0, 0.1); --bs-table-hover-color: #212529; --bs-table-hover-bg: rgba(0, 0, 0, 0.075); width: 100%; margin-bottom: 1rem; color: #212529; vertical-align: top; border-color: #dee2e6; } .table > :not(caption) > * > * { padding: 0.5rem 0.5rem; background-color: var(--bs-table-bg); border-bottom-width: 1px; box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg); } .table > tbody { vertical-align: inherit; } .table > thead { vertical-align: bottom; } .table > :not(:first-child) { border-top: 2px solid currentColor; } .caption-top { caption-side: top; } .table-sm > :not(caption) > * > * { padding: 0.25rem 0.25rem; } .table-bordered > :not(caption) > * { border-width: 1px 0; } .table-bordered > :not(caption) > * > * { border-width: 0 1px; } .table-borderless > :not(caption) > * > * { border-bottom-width: 0; } .table-borderless > :not(:first-child) { border-top-width: 0; } .table-striped > tbody > tr:nth-of-type(odd) > * { --bs-table-accent-bg: var(--bs-table-striped-bg); color: var(--bs-table-striped-color); } .table-active { --bs-table-accent-bg: var(--bs-table-active-bg); color: var(--bs-table-active-color); } .table-hover > tbody > tr:hover > * { --bs-table-accent-bg: var(--bs-table-hover-bg); color: var(--bs-table-hover-color); } .table-primary { --bs-table-bg: #cfe2ff; --bs-table-striped-bg: #c5d7f2; --bs-table-striped-color: #000; --bs-table-active-bg: #bacbe6; --bs-table-active-color: #000; --bs-table-hover-bg: #bfd1ec; --bs-table-hover-color: #000; color: #000; border-color: #bacbe6; } .table-secondary { --bs-table-bg: #e2e3e5; --bs-table-striped-bg: #d7d8da; --bs-table-striped-color: #000; --bs-table-active-bg: #cbccce; --bs-table-active-color: #000; --bs-table-hover-bg: #d1d2d4; --bs-table-hover-color: #000; color: #000; border-color: #cbccce; } .table-success { --bs-table-bg: #d1e7dd; --bs-table-striped-bg: #c7dbd2; --bs-table-striped-color: #000; --bs-table-active-bg: #bcd0c7; --bs-table-active-color: #000; --bs-table-hover-bg: #c1d6cc; --bs-table-hover-color: #000; color: #000; border-color: #bcd0c7; } .table-info { --bs-table-bg: #cff4fc; --bs-table-striped-bg: #c5e8ef; --bs-table-striped-color: #000; --bs-table-active-bg: #badce3; --bs-table-active-color: #000; --bs-table-hover-bg: #bfe2e9; --bs-table-hover-color: #000; color: #000; border-color: #badce3; } .table-warning { --bs-table-bg: #fff3cd; --bs-table-striped-bg: #f2e7c3; --bs-table-striped-color: #000; --bs-table-active-bg: #e6dbb9; --bs-table-active-color: #000; --bs-table-hover-bg: #ece1be; --bs-table-hover-color: #000; color: #000; border-color: #e6dbb9; } .table-danger { --bs-table-bg: #f8d7da; --bs-table-striped-bg: #eccccf; --bs-table-striped-color: #000; --bs-table-active-bg: #dfc2c4; --bs-table-active-color: #000; --bs-table-hover-bg: #e5c7ca; --bs-table-hover-color: #000; color: #000; border-color: #dfc2c4; } .table-light { --bs-table-bg: #f8f9fa; --bs-table-striped-bg: #ecedee; --bs-table-striped-color: #000; --bs-table-active-bg: #dfe0e1; --bs-table-active-color: #000; --bs-table-hover-bg: #e5e6e7; --bs-table-hover-color: #000; color: #000; border-color: #dfe0e1; } .table-dark { --bs-table-bg: #212529; --bs-table-striped-bg: #2c3034; --bs-table-striped-color: #fff; --bs-table-active-bg: #373b3e; --bs-table-active-color: #fff; --bs-table-hover-bg: #323539; --bs-table-hover-color: #fff; color: #fff; border-color: #373b3e; } .table-responsive { overflow-x: auto; -webkit-overflow-scrolling: touch; } @media (max-width: 575.98px) { .table-responsive-sm { overflow-x: auto; -webkit-overflow-scrolling: touch; } } @media (max-width: 767.98px) { .table-responsive-md { overflow-x: auto; -webkit-overflow-scrolling: touch; } } @media (max-width: 991.98px) { .table-responsive-lg { overflow-x: auto; -webkit-overflow-scrolling: touch; } } @media (max-width: 1199.98px) { .table-responsive-xl { overflow-x: auto; -webkit-overflow-scrolling: touch; } } @media (max-width: 1399.98px) { .table-responsive-xxl { overflow-x: auto; -webkit-overflow-scrolling: touch; } } .form-label { margin-bottom: 0.5rem; } .col-form-label { padding-top: calc(0.375rem + 1px); padding-bottom: calc(0.375rem + 1px); margin-bottom: 0; font-size: inherit; line-height: 1.5; } .col-form-label-lg { padding-top: calc(0.5rem + 1px); padding-bottom: calc(0.5rem + 1px); font-size: 1.25rem; } .col-form-label-sm { padding-top: calc(0.25rem + 1px); padding-bottom: calc(0.25rem + 1px); font-size: 0.875rem; } .form-text { margin-top: 0.25rem; font-size: 0.875em; color: #6c757d; } .form-control { display: block; width: 100%; padding: 0.375rem 0.75rem; font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; background-color: #fff; background-clip: padding-box; border: 1px solid #ced4da; -webkit-appearance: none; -moz-appearance: none; appearance: none; border-radius: 0.25rem; transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-control { transition: none; } } .form-control[type="file"] { overflow: hidden; } .form-control[type="file"]:not(:disabled):not([readonly]) { cursor: pointer; } .form-control:focus { color: #212529; background-color: #fff; border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-control::-webkit-date-and-time-value { height: 1.5em; } .form-control::-moz-placeholder { color: #6c757d; opacity: 1; } .form-control::placeholder { color: #6c757d; opacity: 1; } .form-control:disabled, .form-control[readonly] { background-color: #e9ecef; opacity: 1; } .form-control::-webkit-file-upload-button { padding: 0.375rem 0.75rem; margin: -0.375rem -0.75rem; -webkit-margin-end: 0.75rem; margin-inline-end: 0.75rem; color: #212529; background-color: #e9ecef; pointer-events: none; border-color: inherit; border-style: solid; border-width: 0; border-inline-end-width: 1px; border-radius: 0; -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } .form-control::file-selector-button { padding: 0.375rem 0.75rem; margin: -0.375rem -0.75rem; -webkit-margin-end: 0.75rem; margin-inline-end: 0.75rem; color: #212529; background-color: #e9ecef; pointer-events: none; border-color: inherit; border-style: solid; border-width: 0; border-inline-end-width: 1px; border-radius: 0; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-control::-webkit-file-upload-button { -webkit-transition: none; transition: none; } .form-control::file-selector-button { transition: none; } } .form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button { background-color: #dde0e3; } .form-control:hover:not(:disabled):not([readonly])::file-selector-button { background-color: #dde0e3; } .form-control::-webkit-file-upload-button { padding: 0.375rem 0.75rem; margin: -0.375rem -0.75rem; -webkit-margin-end: 0.75rem; margin-inline-end: 0.75rem; color: #212529; background-color: #e9ecef; pointer-events: none; border-color: inherit; border-style: solid; border-width: 0; border-inline-end-width: 1px; border-radius: 0; -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-control::-webkit-file-upload-button { -webkit-transition: none; transition: none; } } .form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button { background-color: #dde0e3; } .form-control-plaintext { display: block; width: 100%; padding: 0.375rem 0; margin-bottom: 0; line-height: 1.5; color: #212529; background-color: transparent; border: solid transparent; border-width: 1px 0; } .form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { padding-right: 0; padding-left: 0; } .form-control-sm { min-height: calc(1.5em + 0.5rem + 2px); padding: 0.25rem 0.5rem; font-size: 0.875rem; border-radius: 0.2rem; } .form-control-sm::-webkit-file-upload-button { padding: 0.25rem 0.5rem; margin: -0.25rem -0.5rem; -webkit-margin-end: 0.5rem; margin-inline-end: 0.5rem; } .form-control-sm::file-selector-button { padding: 0.25rem 0.5rem; margin: -0.25rem -0.5rem; -webkit-margin-end: 0.5rem; margin-inline-end: 0.5rem; } .form-control-sm::-webkit-file-upload-button { padding: 0.25rem 0.5rem; margin: -0.25rem -0.5rem; -webkit-margin-end: 0.5rem; margin-inline-end: 0.5rem; } .form-control-lg { min-height: calc(1.5em + 1rem + 2px); padding: 0.5rem 1rem; font-size: 1.25rem; border-radius: 0.3rem; } .form-control-lg::-webkit-file-upload-button { padding: 0.5rem 1rem; margin: -0.5rem -1rem; -webkit-margin-end: 1rem; margin-inline-end: 1rem; } .form-control-lg::file-selector-button { padding: 0.5rem 1rem; margin: -0.5rem -1rem; -webkit-margin-end: 1rem; margin-inline-end: 1rem; } .form-control-lg::-webkit-file-upload-button { padding: 0.5rem 1rem; margin: -0.5rem -1rem; -webkit-margin-end: 1rem; margin-inline-end: 1rem; } textarea.form-control { min-height: calc(1.5em + 0.75rem + 2px); } textarea.form-control-sm { min-height: calc(1.5em + 0.5rem + 2px); } textarea.form-control-lg { min-height: calc(1.5em + 1rem + 2px); } .form-control-color { width: 3rem; height: auto; padding: 0.375rem; } .form-control-color:not(:disabled):not([readonly]) { cursor: pointer; } .form-control-color::-moz-color-swatch { height: 1.5em; border-radius: 0.25rem; } .form-control-color::-webkit-color-swatch { height: 1.5em; border-radius: 0.25rem; } .form-select { display: block; width: 100%; padding: 0.375rem 2.25rem 0.375rem 0.75rem; -moz-padding-start: calc(0.75rem - 3px); font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; background-color: #fff; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right 0.75rem center; background-size: 16px 12px; border: 1px solid #ced4da; border-radius: 0.25rem; transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -webkit-appearance: none; -moz-appearance: none; appearance: none; } @media (prefers-reduced-motion: reduce) { .form-select { transition: none; } } .form-select:focus { border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-select[multiple], .form-select[size]:not([size="1"]) { padding-right: 0.75rem; background-image: none; } .form-select:disabled { background-color: #e9ecef; } .form-select:-moz-focusring { color: transparent; text-shadow: 0 0 0 #212529; } .form-select-sm { padding-top: 0.25rem; padding-bottom: 0.25rem; padding-left: 0.5rem; font-size: 0.875rem; border-radius: 0.2rem; } .form-select-lg { padding-top: 0.5rem; padding-bottom: 0.5rem; padding-left: 1rem; font-size: 1.25rem; border-radius: 0.3rem; } .form-check { display: block; min-height: 1.5rem; padding-left: 1.5em; margin-bottom: 0.125rem; } .form-check .form-check-input { float: left; margin-left: -1.5em; } .form-check-input { width: 1em; height: 1em; margin-top: 0.25em; vertical-align: top; background-color: #fff; background-repeat: no-repeat; background-position: center; background-size: contain; border: 1px solid rgba(0, 0, 0, 0.25); -webkit-appearance: none; -moz-appearance: none; appearance: none; -webkit-print-color-adjust: exact; color-adjust: exact; } .form-check-input[type="checkbox"] { border-radius: 0.25em; } .form-check-input[type="radio"] { border-radius: 50%; } .form-check-input:active { filter: brightness(90%); } .form-check-input:focus { border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-check-input:checked { background-color: #0d6efd; border-color: #0d6efd; } .form-check-input:checked[type="checkbox"] { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e"); } .form-check-input:checked[type="radio"] { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); } .form-check-input[type="checkbox"]:indeterminate { background-color: #0d6efd; border-color: #0d6efd; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"); } .form-check-input:disabled { pointer-events: none; filter: none; opacity: 0.5; } .form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { opacity: 0.5; } .form-switch { padding-left: 2.5em; } .form-switch .form-check-input { width: 2em; margin-left: -2.5em; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); background-position: left center; border-radius: 2em; transition: background-position 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-switch .form-check-input { transition: none; } } .form-switch .form-check-input:focus { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e"); } .form-switch .form-check-input:checked { background-position: right center; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } .form-check-inline { display: inline-block; margin-right: 1rem; } .btn-check { position: absolute; clip: rect(0, 0, 0, 0); pointer-events: none; } .btn-check[disabled] + .btn, .btn-check:disabled + .btn { pointer-events: none; filter: none; opacity: 0.65; } .form-range { width: 100%; height: 1.5rem; padding: 0; background-color: transparent; -webkit-appearance: none; -moz-appearance: none; appearance: none; } .form-range:focus { outline: 0; } .form-range:focus::-webkit-slider-thumb { box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-range:focus::-moz-range-thumb { box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-range::-moz-focus-outer { border: 0; } .form-range::-webkit-slider-thumb { width: 1rem; height: 1rem; margin-top: -0.25rem; background-color: #0d6efd; border: 0; border-radius: 1rem; -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -webkit-appearance: none; appearance: none; } @media (prefers-reduced-motion: reduce) { .form-range::-webkit-slider-thumb { -webkit-transition: none; transition: none; } } .form-range::-webkit-slider-thumb:active { background-color: #b6d4fe; } .form-range::-webkit-slider-runnable-track { width: 100%; height: 0.5rem; color: transparent; cursor: pointer; background-color: #dee2e6; border-color: transparent; border-radius: 1rem; } .form-range::-moz-range-thumb { width: 1rem; height: 1rem; background-color: #0d6efd; border: 0; border-radius: 1rem; -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -moz-appearance: none; appearance: none; } @media (prefers-reduced-motion: reduce) { .form-range::-moz-range-thumb { -moz-transition: none; transition: none; } } .form-range::-moz-range-thumb:active { background-color: #b6d4fe; } .form-range::-moz-range-track { width: 100%; height: 0.5rem; color: transparent; cursor: pointer; background-color: #dee2e6; border-color: transparent; border-radius: 1rem; } .form-range:disabled { pointer-events: none; } .form-range:disabled::-webkit-slider-thumb { background-color: #adb5bd; } .form-range:disabled::-moz-range-thumb { background-color: #adb5bd; } .form-floating { position: relative; } .form-floating > .form-control, .form-floating > .form-select { height: calc(3.5rem + 2px); line-height: 1.25; } .form-floating > label { position: absolute; top: 0; left: 0; height: 100%; padding: 1rem 0.75rem; pointer-events: none; border: 1px solid transparent; transform-origin: 0 0; transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-floating > label { transition: none; } } .form-floating > .form-control { padding: 1rem 0.75rem; } .form-floating > .form-control::-moz-placeholder { color: transparent; } .form-floating > .form-control::placeholder { color: transparent; } .form-floating > .form-control:not(:-moz-placeholder-shown) { padding-top: 1.625rem; padding-bottom: 0.625rem; } .form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown) { padding-top: 1.625rem; padding-bottom: 0.625rem; } .form-floating > .form-control:-webkit-autofill { padding-top: 1.625rem; padding-bottom: 0.625rem; } .form-floating > .form-select { padding-top: 1.625rem; padding-bottom: 0.625rem; } .form-floating > .form-control:not(:-moz-placeholder-shown) ~ label { opacity: 0.65; transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } .form-floating > .form-control:focus ~ label, .form-floating > .form-control:not(:placeholder-shown) ~ label, .form-floating > .form-select ~ label { opacity: 0.65; transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } .form-floating > .form-control:-webkit-autofill ~ label { opacity: 0.65; transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } .input-group { position: relative; display: flex; flex-wrap: wrap; align-items: stretch; width: 100%; } .input-group > .form-control, .input-group > .form-select { position: relative; flex: 1 1 auto; width: 1%; min-width: 0; } .input-group > .form-control:focus, .input-group > .form-select:focus { z-index: 3; } .input-group .btn { position: relative; z-index: 2; } .input-group .btn:focus { z-index: 3; } .input-group-text { display: flex; align-items: center; padding: 0.375rem 0.75rem; font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; text-align: center; white-space: nowrap; background-color: #e9ecef; border: 1px solid #ced4da; border-radius: 0.25rem; } .input-group-lg > .form-control, .input-group-lg > .form-select, .input-group-lg > .input-group-text, .input-group-lg > .btn { padding: 0.5rem 1rem; font-size: 1.25rem; border-radius: 0.3rem; } .input-group-sm > .form-control, .input-group-sm > .form-select, .input-group-sm > .input-group-text, .input-group-sm > .btn { padding: 0.25rem 0.5rem; font-size: 0.875rem; border-radius: 0.2rem; } .input-group-lg > .form-select, .input-group-sm > .form-select { padding-right: 3rem; } .input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu), .input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n + 3) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group.has-validation > :nth-last-child(n + 3):not(.dropdown-toggle):not(.dropdown-menu), .input-group.has-validation > .dropdown-toggle:nth-last-child(n + 4) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) { margin-left: -1px; border-top-left-radius: 0; border-bottom-left-radius: 0; } .valid-feedback { display: none; width: 100%; margin-top: 0.25rem; font-size: 0.875em; color: #198754; } .valid-tooltip { position: absolute; top: 100%; z-index: 5; display: none; max-width: 100%; padding: 0.25rem 0.5rem; margin-top: 0.1rem; font-size: 0.875rem; color: #fff; background-color: rgba(25, 135, 84, 0.9); border-radius: 0.25rem; } .was-validated :valid ~ .valid-feedback, .was-validated :valid ~ .valid-tooltip, .is-valid ~ .valid-feedback, .is-valid ~ .valid-tooltip { display: block; } .was-validated .form-control:valid, .form-control.is-valid { border-color: #198754; padding-right: calc(1.5em + 0.75rem); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:valid:focus, .form-control.is-valid:focus { border-color: #198754; box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); } .was-validated textarea.form-control:valid, textarea.form-control.is-valid { padding-right: calc(1.5em + 0.75rem); background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } .was-validated .form-select:valid, .form-select.is-valid { border-color: #198754; } .was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size="1"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size="1"] { padding-right: 4.125rem; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); background-position: right 0.75rem center, center right 2.25rem; background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-select:valid:focus, .form-select.is-valid:focus { border-color: #198754; box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); } .was-validated .form-check-input:valid, .form-check-input.is-valid { border-color: #198754; } .was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked { background-color: #198754; } .was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus { box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); } .was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { color: #198754; } .form-check-inline .form-check-input ~ .valid-feedback { margin-left: 0.5em; } .was-validated .input-group .form-control:valid, .input-group .form-control.is-valid, .was-validated .input-group .form-select:valid, .input-group .form-select.is-valid { z-index: 1; } .was-validated .input-group .form-control:valid:focus, .input-group .form-control.is-valid:focus, .was-validated .input-group .form-select:valid:focus, .input-group .form-select.is-valid:focus { z-index: 3; } .invalid-feedback { display: none; width: 100%; margin-top: 0.25rem; font-size: 0.875em; color: #dc3545; } .invalid-tooltip { position: absolute; top: 100%; z-index: 5; display: none; max-width: 100%; padding: 0.25rem 0.5rem; margin-top: 0.1rem; font-size: 0.875rem; color: #fff; background-color: rgba(220, 53, 69, 0.9); border-radius: 0.25rem; } .was-validated :invalid ~ .invalid-feedback, .was-validated :invalid ~ .invalid-tooltip, .is-invalid ~ .invalid-feedback, .is-invalid ~ .invalid-tooltip { display: block; } .was-validated .form-control:invalid, .form-control.is-invalid { border-color: #dc3545; padding-right: calc(1.5em + 0.75rem); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); } .was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { padding-right: calc(1.5em + 0.75rem); background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } .was-validated .form-select:invalid, .form-select.is-invalid { border-color: #dc3545; } .was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size="1"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size="1"] { padding-right: 4.125rem; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-position: right 0.75rem center, center right 2.25rem; background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-select:invalid:focus, .form-select.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); } .was-validated .form-check-input:invalid, .form-check-input.is-invalid { border-color: #dc3545; } .was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked { background-color: #dc3545; } .was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus { box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); } .was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { color: #dc3545; } .form-check-inline .form-check-input ~ .invalid-feedback { margin-left: 0.5em; } .was-validated .input-group .form-control:invalid, .input-group .form-control.is-invalid, .was-validated .input-group .form-select:invalid, .input-group .form-select.is-invalid { z-index: 2; } .was-validated .input-group .form-control:invalid:focus, .input-group .form-control.is-invalid:focus, .was-validated .input-group .form-select:invalid:focus, .input-group .form-select.is-invalid:focus { z-index: 3; } .btn { display: inline-block; font-weight: 400; line-height: 1.5; color: #212529; text-align: center; text-decoration: none; vertical-align: middle; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; user-select: none; background-color: transparent; border: 1px solid transparent; padding: 0.375rem 0.75rem; font-size: 1rem; border-radius: 0.25rem; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .btn { transition: none; } } .btn:hover { color: #212529; } .btn-check:focus + .btn, .btn:focus { outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .btn:disabled, .btn.disabled, fieldset:disabled .btn { pointer-events: none; opacity: 0.65; } .btn-primary { color: #fff; background-color: #0d6efd; border-color: #0d6efd; } .btn-primary:hover { color: #fff; background-color: #0b5ed7; border-color: #0a58ca; } .btn-check:focus + .btn-primary, .btn-primary:focus { color: #fff; background-color: #0b5ed7; border-color: #0a58ca; box-shadow: 0 0 0 0.25rem rgba(49, 132, 253, 0.5); } .btn-check:checked + .btn-primary, .btn-check:active + .btn-primary, .btn-primary:active, .btn-primary.active, .show > .btn-primary.dropdown-toggle { color: #fff; background-color: #0a58ca; border-color: #0a53be; } .btn-check:checked + .btn-primary:focus, .btn-check:active + .btn-primary:focus, .btn-primary:active:focus, .btn-primary.active:focus, .show > .btn-primary.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(49, 132, 253, 0.5); } .btn-primary:disabled, .btn-primary.disabled { color: #fff; background-color: #0d6efd; border-color: #0d6efd; } .btn-secondary { color: #fff; background-color: #6c757d; border-color: #6c757d; } .btn-secondary:hover { color: #fff; background-color: #5c636a; border-color: #565e64; } .btn-check:focus + .btn-secondary, .btn-secondary:focus { color: #fff; background-color: #5c636a; border-color: #565e64; box-shadow: 0 0 0 0.25rem rgba(130, 138, 145, 0.5); } .btn-check:checked + .btn-secondary, .btn-check:active + .btn-secondary, .btn-secondary:active, .btn-secondary.active, .show > .btn-secondary.dropdown-toggle { color: #fff; background-color: #565e64; border-color: #51585e; } .btn-check:checked + .btn-secondary:focus, .btn-check:active + .btn-secondary:focus, .btn-secondary:active:focus, .btn-secondary.active:focus, .show > .btn-secondary.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(130, 138, 145, 0.5); } .btn-secondary:disabled, .btn-secondary.disabled { color: #fff; background-color: #6c757d; border-color: #6c757d; } .btn-success { color: #fff; background-color: #198754; border-color: #198754; } .btn-success:hover { color: #fff; background-color: #157347; border-color: #146c43; } .btn-check:focus + .btn-success, .btn-success:focus { color: #fff; background-color: #157347; border-color: #146c43; box-shadow: 0 0 0 0.25rem rgba(60, 153, 110, 0.5); } .btn-check:checked + .btn-success, .btn-check:active + .btn-success, .btn-success:active, .btn-success.active, .show > .btn-success.dropdown-toggle { color: #fff; background-color: #146c43; border-color: #13653f; } .btn-check:checked + .btn-success:focus, .btn-check:active + .btn-success:focus, .btn-success:active:focus, .btn-success.active:focus, .show > .btn-success.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(60, 153, 110, 0.5); } .btn-success:disabled, .btn-success.disabled { color: #fff; background-color: #198754; border-color: #198754; } .btn-info { color: #000; background-color: #0dcaf0; border-color: #0dcaf0; } .btn-info:hover { color: #000; background-color: #31d2f2; border-color: #25cff2; } .btn-check:focus + .btn-info, .btn-info:focus { color: #000; background-color: #31d2f2; border-color: #25cff2; box-shadow: 0 0 0 0.25rem rgba(11, 172, 204, 0.5); } .btn-check:checked + .btn-info, .btn-check:active + .btn-info, .btn-info:active, .btn-info.active, .show > .btn-info.dropdown-toggle { color: #000; background-color: #3dd5f3; border-color: #25cff2; } .btn-check:checked + .btn-info:focus, .btn-check:active + .btn-info:focus, .btn-info:active:focus, .btn-info.active:focus, .show > .btn-info.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(11, 172, 204, 0.5); } .btn-info:disabled, .btn-info.disabled { color: #000; background-color: #0dcaf0; border-color: #0dcaf0; } .btn-warning { color: #000; background-color: #ffc107; border-color: #ffc107; } .btn-warning:hover { color: #000; background-color: #ffca2c; border-color: #ffc720; } .btn-check:focus + .btn-warning, .btn-warning:focus { color: #000; background-color: #ffca2c; border-color: #ffc720; box-shadow: 0 0 0 0.25rem rgba(217, 164, 6, 0.5); } .btn-check:checked + .btn-warning, .btn-check:active + .btn-warning, .btn-warning:active, .btn-warning.active, .show > .btn-warning.dropdown-toggle { color: #000; background-color: #ffcd39; border-color: #ffc720; } .btn-check:checked + .btn-warning:focus, .btn-check:active + .btn-warning:focus, .btn-warning:active:focus, .btn-warning.active:focus, .show > .btn-warning.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(217, 164, 6, 0.5); } .btn-warning:disabled, .btn-warning.disabled { color: #000; background-color: #ffc107; border-color: #ffc107; } .btn-danger { color: #fff; background-color: #dc3545; border-color: #dc3545; } .btn-danger:hover { color: #fff; background-color: #bb2d3b; border-color: #b02a37; } .btn-check:focus + .btn-danger, .btn-danger:focus { color: #fff; background-color: #bb2d3b; border-color: #b02a37; box-shadow: 0 0 0 0.25rem rgba(225, 83, 97, 0.5); } .btn-check:checked + .btn-danger, .btn-check:active + .btn-danger, .btn-danger:active, .btn-danger.active, .show > .btn-danger.dropdown-toggle { color: #fff; background-color: #b02a37; border-color: #a52834; } .btn-check:checked + .btn-danger:focus, .btn-check:active + .btn-danger:focus, .btn-danger:active:focus, .btn-danger.active:focus, .show > .btn-danger.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(225, 83, 97, 0.5); } .btn-danger:disabled, .btn-danger.disabled { color: #fff; background-color: #dc3545; border-color: #dc3545; } .btn-light { color: #000; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-light:hover { color: #000; background-color: #f9fafb; border-color: #f9fafb; } .btn-check:focus + .btn-light, .btn-light:focus { color: #000; background-color: #f9fafb; border-color: #f9fafb; box-shadow: 0 0 0 0.25rem rgba(211, 212, 213, 0.5); } .btn-check:checked + .btn-light, .btn-check:active + .btn-light, .btn-light:active, .btn-light.active, .show > .btn-light.dropdown-toggle { color: #000; background-color: #f9fafb; border-color: #f9fafb; } .btn-check:checked + .btn-light:focus, .btn-check:active + .btn-light:focus, .btn-light:active:focus, .btn-light.active:focus, .show > .btn-light.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(211, 212, 213, 0.5); } .btn-light:disabled, .btn-light.disabled { color: #000; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-dark { color: #fff; background-color: #212529; border-color: #212529; } .btn-dark:hover { color: #fff; background-color: #1c1f23; border-color: #1a1e21; } .btn-check:focus + .btn-dark, .btn-dark:focus { color: #fff; background-color: #1c1f23; border-color: #1a1e21; box-shadow: 0 0 0 0.25rem rgba(66, 70, 73, 0.5); } .btn-check:checked + .btn-dark, .btn-check:active + .btn-dark, .btn-dark:active, .btn-dark.active, .show > .btn-dark.dropdown-toggle { color: #fff; background-color: #1a1e21; border-color: #191c1f; } .btn-check:checked + .btn-dark:focus, .btn-check:active + .btn-dark:focus, .btn-dark:active:focus, .btn-dark.active:focus, .show > .btn-dark.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(66, 70, 73, 0.5); } .btn-dark:disabled, .btn-dark.disabled { color: #fff; background-color: #212529; border-color: #212529; } .btn-outline-primary { color: #0d6efd; border-color: #0d6efd; } .btn-outline-primary:hover { color: #fff; background-color: #0d6efd; border-color: #0d6efd; } .btn-check:focus + .btn-outline-primary, .btn-outline-primary:focus { box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.5); } .btn-check:checked + .btn-outline-primary, .btn-check:active + .btn-outline-primary, .btn-outline-primary:active, .btn-outline-primary.active, .btn-outline-primary.dropdown-toggle.show { color: #fff; background-color: #0d6efd; border-color: #0d6efd; } .btn-check:checked + .btn-outline-primary:focus, .btn-check:active + .btn-outline-primary:focus, .btn-outline-primary:active:focus, .btn-outline-primary.active:focus, .btn-outline-primary.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.5); } .btn-outline-primary:disabled, .btn-outline-primary.disabled { color: #0d6efd; background-color: transparent; } .btn-outline-secondary { color: #6c757d; border-color: #6c757d; } .btn-outline-secondary:hover { color: #fff; background-color: #6c757d; border-color: #6c757d; } .btn-check:focus + .btn-outline-secondary, .btn-outline-secondary:focus { box-shadow: 0 0 0 0.25rem rgba(108, 117, 125, 0.5); } .btn-check:checked + .btn-outline-secondary, .btn-check:active + .btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show { color: #fff; background-color: #6c757d; border-color: #6c757d; } .btn-check:checked + .btn-outline-secondary:focus, .btn-check:active + .btn-outline-secondary:focus, .btn-outline-secondary:active:focus, .btn-outline-secondary.active:focus, .btn-outline-secondary.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(108, 117, 125, 0.5); } .btn-outline-secondary:disabled, .btn-outline-secondary.disabled { color: #6c757d; background-color: transparent; } .btn-outline-success { color: #198754; border-color: #198754; } .btn-outline-success:hover { color: #fff; background-color: #198754; border-color: #198754; } .btn-check:focus + .btn-outline-success, .btn-outline-success:focus { box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.5); } .btn-check:checked + .btn-outline-success, .btn-check:active + .btn-outline-success, .btn-outline-success:active, .btn-outline-success.active, .btn-outline-success.dropdown-toggle.show { color: #fff; background-color: #198754; border-color: #198754; } .btn-check:checked + .btn-outline-success:focus, .btn-check:active + .btn-outline-success:focus, .btn-outline-success:active:focus, .btn-outline-success.active:focus, .btn-outline-success.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.5); } .btn-outline-success:disabled, .btn-outline-success.disabled { color: #198754; background-color: transparent; } .btn-outline-info { color: #0dcaf0; border-color: #0dcaf0; } .btn-outline-info:hover { color: #000; background-color: #0dcaf0; border-color: #0dcaf0; } .btn-check:focus + .btn-outline-info, .btn-outline-info:focus { box-shadow: 0 0 0 0.25rem rgba(13, 202, 240, 0.5); } .btn-check:checked + .btn-outline-info, .btn-check:active + .btn-outline-info, .btn-outline-info:active, .btn-outline-info.active, .btn-outline-info.dropdown-toggle.show { color: #000; background-color: #0dcaf0; border-color: #0dcaf0; } .btn-check:checked + .btn-outline-info:focus, .btn-check:active + .btn-outline-info:focus, .btn-outline-info:active:focus, .btn-outline-info.active:focus, .btn-outline-info.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(13, 202, 240, 0.5); } .btn-outline-info:disabled, .btn-outline-info.disabled { color: #0dcaf0; background-color: transparent; } .btn-outline-warning { color: #ffc107; border-color: #ffc107; } .btn-outline-warning:hover { color: #000; background-color: #ffc107; border-color: #ffc107; } .btn-check:focus + .btn-outline-warning, .btn-outline-warning:focus { box-shadow: 0 0 0 0.25rem rgba(255, 193, 7, 0.5); } .btn-check:checked + .btn-outline-warning, .btn-check:active + .btn-outline-warning, .btn-outline-warning:active, .btn-outline-warning.active, .btn-outline-warning.dropdown-toggle.show { color: #000; background-color: #ffc107; border-color: #ffc107; } .btn-check:checked + .btn-outline-warning:focus, .btn-check:active + .btn-outline-warning:focus, .btn-outline-warning:active:focus, .btn-outline-warning.active:focus, .btn-outline-warning.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(255, 193, 7, 0.5); } .btn-outline-warning:disabled, .btn-outline-warning.disabled { color: #ffc107; background-color: transparent; } .btn-outline-danger { color: #dc3545; border-color: #dc3545; } .btn-outline-danger:hover { color: #fff; background-color: #dc3545; border-color: #dc3545; } .btn-check:focus + .btn-outline-danger, .btn-outline-danger:focus { box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.5); } .btn-check:checked + .btn-outline-danger, .btn-check:active + .btn-outline-danger, .btn-outline-danger:active, .btn-outline-danger.active, .btn-outline-danger.dropdown-toggle.show { color: #fff; background-color: #dc3545; border-color: #dc3545; } .btn-check:checked + .btn-outline-danger:focus, .btn-check:active + .btn-outline-danger:focus, .btn-outline-danger:active:focus, .btn-outline-danger.active:focus, .btn-outline-danger.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.5); } .btn-outline-danger:disabled, .btn-outline-danger.disabled { color: #dc3545; background-color: transparent; } .btn-outline-light { color: #f8f9fa; border-color: #f8f9fa; } .btn-outline-light:hover { color: #000; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-check:focus + .btn-outline-light, .btn-outline-light:focus { box-shadow: 0 0 0 0.25rem rgba(248, 249, 250, 0.5); } .btn-check:checked + .btn-outline-light, .btn-check:active + .btn-outline-light, .btn-outline-light:active, .btn-outline-light.active, .btn-outline-light.dropdown-toggle.show { color: #000; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-check:checked + .btn-outline-light:focus, .btn-check:active + .btn-outline-light:focus, .btn-outline-light:active:focus, .btn-outline-light.active:focus, .btn-outline-light.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(248, 249, 250, 0.5); } .btn-outline-light:disabled, .btn-outline-light.disabled { color: #f8f9fa; background-color: transparent; } .btn-outline-dark { color: #212529; border-color: #212529; } .btn-outline-dark:hover { color: #fff; background-color: #212529; border-color: #212529; } .btn-check:focus + .btn-outline-dark, .btn-outline-dark:focus { box-shadow: 0 0 0 0.25rem rgba(33, 37, 41, 0.5); } .btn-check:checked + .btn-outline-dark, .btn-check:active + .btn-outline-dark, .btn-outline-dark:active, .btn-outline-dark.active, .btn-outline-dark.dropdown-toggle.show { color: #fff; background-color: #212529; border-color: #212529; } .btn-check:checked + .btn-outline-dark:focus, .btn-check:active + .btn-outline-dark:focus, .btn-outline-dark:active:focus, .btn-outline-dark.active:focus, .btn-outline-dark.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(33, 37, 41, 0.5); } .btn-outline-dark:disabled, .btn-outline-dark.disabled { color: #212529; background-color: transparent; } .btn-link { font-weight: 400; color: #0d6efd; text-decoration: underline; } .btn-link:hover { color: #0a58ca; } .btn-link:disabled, .btn-link.disabled { color: #6c757d; } .btn-lg, .btn-group-lg > .btn { padding: 0.5rem 1rem; font-size: 1.25rem; border-radius: 0.3rem; } .btn-sm, .btn-group-sm > .btn { padding: 0.25rem 0.5rem; font-size: 0.875rem; border-radius: 0.2rem; } .fade { transition: opacity 0.15s linear; } @media (prefers-reduced-motion: reduce) { .fade { transition: none; } } .fade:not(.show) { opacity: 0; } .collapse:not(.show) { display: none; } .collapsing { height: 0; overflow: hidden; transition: height 0.35s ease; } @media (prefers-reduced-motion: reduce) { .collapsing { transition: none; } } .collapsing.collapse-horizontal { width: 0; height: auto; transition: width 0.35s ease; } @media (prefers-reduced-motion: reduce) { .collapsing.collapse-horizontal { transition: none; } } .dropup, .dropend, .dropdown, .dropstart { position: relative; } .dropdown-toggle { white-space: nowrap; } .dropdown-toggle::after { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid; border-right: 0.3em solid transparent; border-bottom: 0; border-left: 0.3em solid transparent; } .dropdown-toggle:empty::after { margin-left: 0; } .dropdown-menu { position: absolute; z-index: 1000; display: none; min-width: 10rem; padding: 0.5rem 0; margin: 0; font-size: 1rem; color: #212529; text-align: left; list-style: none; background-color: #fff; background-clip: padding-box; border: 1px solid rgba(0, 0, 0, 0.15); border-radius: 0.25rem; } .dropdown-menu[data-bs-popper] { top: 100%; left: 0; margin-top: 0.125rem; } .dropdown-menu-start { --bs-position: start; } .dropdown-menu-start[data-bs-popper] { right: auto; left: 0; } .dropdown-menu-end { --bs-position: end; } .dropdown-menu-end[data-bs-popper] { right: 0; left: auto; } @media (min-width: 576px) { .dropdown-menu-sm-start { --bs-position: start; } .dropdown-menu-sm-start[data-bs-popper] { right: auto; left: 0; } .dropdown-menu-sm-end { --bs-position: end; } .dropdown-menu-sm-end[data-bs-popper] { right: 0; left: auto; } } @media (min-width: 768px) { .dropdown-menu-md-start { --bs-position: start; } .dropdown-menu-md-start[data-bs-popper] { right: auto; left: 0; } .dropdown-menu-md-end { --bs-position: end; } .dropdown-menu-md-end[data-bs-popper] { right: 0; left: auto; } } @media (min-width: 992px) { .dropdown-menu-lg-start { --bs-position: start; } .dropdown-menu-lg-start[data-bs-popper] { right: auto; left: 0; } .dropdown-menu-lg-end { --bs-position: end; } .dropdown-menu-lg-end[data-bs-popper] { right: 0; left: auto; } } @media (min-width: 1200px) { .dropdown-menu-xl-start { --bs-position: start; } .dropdown-menu-xl-start[data-bs-popper] { right: auto; left: 0; } .dropdown-menu-xl-end { --bs-position: end; } .dropdown-menu-xl-end[data-bs-popper] { right: 0; left: auto; } } @media (min-width: 1400px) { .dropdown-menu-xxl-start { --bs-position: start; } .dropdown-menu-xxl-start[data-bs-popper] { right: auto; left: 0; } .dropdown-menu-xxl-end { --bs-position: end; } .dropdown-menu-xxl-end[data-bs-popper] { right: 0; left: auto; } } .dropup .dropdown-menu[data-bs-popper] { top: auto; bottom: 100%; margin-top: 0; margin-bottom: 0.125rem; } .dropup .dropdown-toggle::after { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0; border-right: 0.3em solid transparent; border-bottom: 0.3em solid; border-left: 0.3em solid transparent; } .dropup .dropdown-toggle:empty::after { margin-left: 0; } .dropend .dropdown-menu[data-bs-popper] { top: 0; right: auto; left: 100%; margin-top: 0; margin-left: 0.125rem; } .dropend .dropdown-toggle::after { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid transparent; border-right: 0; border-bottom: 0.3em solid transparent; border-left: 0.3em solid; } .dropend .dropdown-toggle:empty::after { margin-left: 0; } .dropend .dropdown-toggle::after { vertical-align: 0; } .dropstart .dropdown-menu[data-bs-popper] { top: 0; right: 100%; left: auto; margin-top: 0; margin-right: 0.125rem; } .dropstart .dropdown-toggle::after { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; } .dropstart .dropdown-toggle::after { display: none; } .dropstart .dropdown-toggle::before { display: inline-block; margin-right: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid transparent; border-right: 0.3em solid; border-bottom: 0.3em solid transparent; } .dropstart .dropdown-toggle:empty::after { margin-left: 0; } .dropstart .dropdown-toggle::before { vertical-align: 0; } .dropdown-divider { height: 0; margin: 0.5rem 0; overflow: hidden; border-top: 1px solid rgba(0, 0, 0, 0.15); } .dropdown-item { display: block; width: 100%; padding: 0.25rem 1rem; clear: both; font-weight: 400; color: #212529; text-align: inherit; text-decoration: none; white-space: nowrap; background-color: transparent; border: 0; } .dropdown-item:hover, .dropdown-item:focus { color: #1e2125; background-color: #e9ecef; } .dropdown-item.active, .dropdown-item:active { color: #fff; text-decoration: none; background-color: #0d6efd; } .dropdown-item.disabled, .dropdown-item:disabled { color: #adb5bd; pointer-events: none; background-color: transparent; } .dropdown-menu.show { display: block; } .dropdown-header { display: block; padding: 0.5rem 1rem; margin-bottom: 0; font-size: 0.875rem; color: #6c757d; white-space: nowrap; } .dropdown-item-text { display: block; padding: 0.25rem 1rem; color: #212529; } .dropdown-menu-dark { color: #dee2e6; background-color: #343a40; border-color: rgba(0, 0, 0, 0.15); } .dropdown-menu-dark .dropdown-item { color: #dee2e6; } .dropdown-menu-dark .dropdown-item:hover, .dropdown-menu-dark .dropdown-item:focus { color: #fff; background-color: rgba(255, 255, 255, 0.15); } .dropdown-menu-dark .dropdown-item.active, .dropdown-menu-dark .dropdown-item:active { color: #fff; background-color: #0d6efd; } .dropdown-menu-dark .dropdown-item.disabled, .dropdown-menu-dark .dropdown-item:disabled { color: #adb5bd; } .dropdown-menu-dark .dropdown-divider { border-color: rgba(0, 0, 0, 0.15); } .dropdown-menu-dark .dropdown-item-text { color: #dee2e6; } .dropdown-menu-dark .dropdown-header { color: #adb5bd; } .btn-group, .btn-group-vertical { position: relative; display: inline-flex; vertical-align: middle; } .btn-group > .btn, .btn-group-vertical > .btn { position: relative; flex: 1 1 auto; } .btn-group > .btn-check:checked + .btn, .btn-group > .btn-check:focus + .btn, .btn-group > .btn:hover, .btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, .btn-group-vertical > .btn-check:checked + .btn, .btn-group-vertical > .btn-check:focus + .btn, .btn-group-vertical > .btn:hover, .btn-group-vertical > .btn:focus, .btn-group-vertical > .btn:active, .btn-group-vertical > .btn.active { z-index: 1; } .btn-toolbar { display: flex; flex-wrap: wrap; justify-content: flex-start; } .btn-toolbar .input-group { width: auto; } .btn-group > .btn:not(:first-child), .btn-group > .btn-group:not(:first-child) { margin-left: -1px; } .btn-group > .btn:not(:last-child):not(.dropdown-toggle), .btn-group > .btn-group:not(:last-child) > .btn { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn:nth-child(n + 3), .btn-group > :not(.btn-check) + .btn, .btn-group > .btn-group:not(:first-child) > .btn { border-top-left-radius: 0; border-bottom-left-radius: 0; } .dropdown-toggle-split { padding-right: 0.5625rem; padding-left: 0.5625rem; } .dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after { margin-left: 0; } .dropstart .dropdown-toggle-split::before { margin-right: 0; } .btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { padding-right: 0.375rem; padding-left: 0.375rem; } .btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { padding-right: 0.75rem; padding-left: 0.75rem; } .btn-group-vertical { flex-direction: column; align-items: flex-start; justify-content: center; } .btn-group-vertical > .btn, .btn-group-vertical > .btn-group { width: 100%; } .btn-group-vertical > .btn:not(:first-child), .btn-group-vertical > .btn-group:not(:first-child) { margin-top: -1px; } .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), .btn-group-vertical > .btn-group:not(:last-child) > .btn { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn ~ .btn, .btn-group-vertical > .btn-group:not(:first-child) > .btn { border-top-left-radius: 0; border-top-right-radius: 0; } .nav { display: flex; flex-wrap: wrap; padding-left: 0; margin-bottom: 0; list-style: none; } .nav-link { display: block; padding: 0.5rem 1rem; color: #0d6efd; text-decoration: none; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .nav-link { transition: none; } } .nav-link:hover, .nav-link:focus { color: #0a58ca; } .nav-link.disabled { color: #6c757d; pointer-events: none; cursor: default; } .nav-tabs { border-bottom: 1px solid #dee2e6; } .nav-tabs .nav-link { margin-bottom: -1px; background: none; border: 1px solid transparent; border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; } .nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { border-color: #e9ecef #e9ecef #dee2e6; isolation: isolate; } .nav-tabs .nav-link.disabled { color: #6c757d; background-color: transparent; border-color: transparent; } .nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link { color: #495057; background-color: #fff; border-color: #dee2e6 #dee2e6 #fff; } .nav-tabs .dropdown-menu { margin-top: -1px; border-top-left-radius: 0; border-top-right-radius: 0; } .nav-pills .nav-link { background: none; border: 0; border-radius: 0.25rem; } .nav-pills .nav-link.active, .nav-pills .show > .nav-link { color: #fff; background-color: #0d6efd; } .nav-fill > .nav-link, .nav-fill .nav-item { flex: 1 1 auto; text-align: center; } .nav-justified > .nav-link, .nav-justified .nav-item { flex-basis: 0; flex-grow: 1; text-align: center; } .nav-fill .nav-item .nav-link, .nav-justified .nav-item .nav-link { width: 100%; } .tab-content > .tab-pane { display: none; } .tab-content > .active { display: block; } .navbar { position: relative; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; padding-top: 0.5rem; padding-bottom: 0.5rem; } .navbar > .container, .navbar > .container-fluid, .navbar > .container-sm, .navbar > .container-md, .navbar > .container-lg, .navbar > .container-xl, .navbar > .container-xxl { display: flex; flex-wrap: inherit; align-items: center; justify-content: space-between; } .navbar-brand { padding-top: 0.3125rem; padding-bottom: 0.3125rem; margin-right: 1rem; font-size: 1.25rem; text-decoration: none; white-space: nowrap; } .navbar-nav { display: flex; flex-direction: column; padding-left: 0; margin-bottom: 0; list-style: none; } .navbar-nav .nav-link { padding-right: 0; padding-left: 0; } .navbar-nav .dropdown-menu { position: static; } .navbar-text { padding-top: 0.5rem; padding-bottom: 0.5rem; } .navbar-collapse { flex-basis: 100%; flex-grow: 1; align-items: center; } .navbar-toggler { padding: 0.25rem 0.75rem; font-size: 1.25rem; line-height: 1; background-color: transparent; border: 1px solid transparent; border-radius: 0.25rem; transition: box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .navbar-toggler { transition: none; } } .navbar-toggler:hover { text-decoration: none; } .navbar-toggler:focus { text-decoration: none; outline: 0; box-shadow: 0 0 0 0.25rem; } .navbar-toggler-icon { display: inline-block; width: 1.5em; height: 1.5em; vertical-align: middle; background-repeat: no-repeat; background-position: center; background-size: 100%; } .navbar-nav-scroll { max-height: var(--bs-scroll-height, 75vh); overflow-y: auto; } @media (min-width: 576px) { .navbar-expand-sm { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-sm .navbar-nav { flex-direction: row; } .navbar-expand-sm .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-sm .navbar-nav .nav-link { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-sm .navbar-nav-scroll { overflow: visible; } .navbar-expand-sm .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-sm .navbar-toggler { display: none; } .navbar-expand-sm .offcanvas-header { display: none; } .navbar-expand-sm .offcanvas { position: inherit; bottom: 0; z-index: 1000; flex-grow: 1; visibility: visible !important; background-color: transparent; border-right: 0; border-left: 0; transition: none; transform: none; } .navbar-expand-sm .offcanvas-top, .navbar-expand-sm .offcanvas-bottom { height: auto; border-top: 0; border-bottom: 0; } .navbar-expand-sm .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } @media (min-width: 768px) { .navbar-expand-md { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-md .navbar-nav { flex-direction: row; } .navbar-expand-md .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-md .navbar-nav .nav-link { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-md .navbar-nav-scroll { overflow: visible; } .navbar-expand-md .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-md .navbar-toggler { display: none; } .navbar-expand-md .offcanvas-header { display: none; } .navbar-expand-md .offcanvas { position: inherit; bottom: 0; z-index: 1000; flex-grow: 1; visibility: visible !important; background-color: transparent; border-right: 0; border-left: 0; transition: none; transform: none; } .navbar-expand-md .offcanvas-top, .navbar-expand-md .offcanvas-bottom { height: auto; border-top: 0; border-bottom: 0; } .navbar-expand-md .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } @media (min-width: 992px) { .navbar-expand-lg { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-lg .navbar-nav { flex-direction: row; } .navbar-expand-lg .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-lg .navbar-nav .nav-link { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-lg .navbar-nav-scroll { overflow: visible; } .navbar-expand-lg .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-lg .navbar-toggler { display: none; } .navbar-expand-lg .offcanvas-header { display: none; } .navbar-expand-lg .offcanvas { position: inherit; bottom: 0; z-index: 1000; flex-grow: 1; visibility: visible !important; background-color: transparent; border-right: 0; border-left: 0; transition: none; transform: none; } .navbar-expand-lg .offcanvas-top, .navbar-expand-lg .offcanvas-bottom { height: auto; border-top: 0; border-bottom: 0; } .navbar-expand-lg .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } @media (min-width: 1200px) { .navbar-expand-xl { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-xl .navbar-nav { flex-direction: row; } .navbar-expand-xl .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-xl .navbar-nav .nav-link { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-xl .navbar-nav-scroll { overflow: visible; } .navbar-expand-xl .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-xl .navbar-toggler { display: none; } .navbar-expand-xl .offcanvas-header { display: none; } .navbar-expand-xl .offcanvas { position: inherit; bottom: 0; z-index: 1000; flex-grow: 1; visibility: visible !important; background-color: transparent; border-right: 0; border-left: 0; transition: none; transform: none; } .navbar-expand-xl .offcanvas-top, .navbar-expand-xl .offcanvas-bottom { height: auto; border-top: 0; border-bottom: 0; } .navbar-expand-xl .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } @media (min-width: 1400px) { .navbar-expand-xxl { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-xxl .navbar-nav { flex-direction: row; } .navbar-expand-xxl .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-xxl .navbar-nav .nav-link { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-xxl .navbar-nav-scroll { overflow: visible; } .navbar-expand-xxl .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-xxl .navbar-toggler { display: none; } .navbar-expand-xxl .offcanvas-header { display: none; } .navbar-expand-xxl .offcanvas { position: inherit; bottom: 0; z-index: 1000; flex-grow: 1; visibility: visible !important; background-color: transparent; border-right: 0; border-left: 0; transition: none; transform: none; } .navbar-expand-xxl .offcanvas-top, .navbar-expand-xxl .offcanvas-bottom { height: auto; border-top: 0; border-bottom: 0; } .navbar-expand-xxl .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } .navbar-expand { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand .navbar-nav { flex-direction: row; } .navbar-expand .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand .navbar-nav .nav-link { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand .navbar-nav-scroll { overflow: visible; } .navbar-expand .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand .navbar-toggler { display: none; } .navbar-expand .offcanvas-header { display: none; } .navbar-expand .offcanvas { position: inherit; bottom: 0; z-index: 1000; flex-grow: 1; visibility: visible !important; background-color: transparent; border-right: 0; border-left: 0; transition: none; transform: none; } .navbar-expand .offcanvas-top, .navbar-expand .offcanvas-bottom { height: auto; border-top: 0; border-bottom: 0; } .navbar-expand .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } .navbar-light .navbar-brand { color: rgba(0, 0, 0, 0.9); } .navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { color: rgba(0, 0, 0, 0.9); } .navbar-light .navbar-nav .nav-link { color: rgba(0, 0, 0, 0.55); } .navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { color: rgba(0, 0, 0, 0.7); } .navbar-light .navbar-nav .nav-link.disabled { color: rgba(0, 0, 0, 0.3); } .navbar-light .navbar-nav .show > .nav-link, .navbar-light .navbar-nav .nav-link.active { color: rgba(0, 0, 0, 0.9); } .navbar-light .navbar-toggler { color: rgba(0, 0, 0, 0.55); border-color: rgba(0, 0, 0, 0.1); } .navbar-light .navbar-toggler-icon { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .navbar-light .navbar-text { color: rgba(0, 0, 0, 0.55); } .navbar-light .navbar-text a, .navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { color: rgba(0, 0, 0, 0.9); } .navbar-dark .navbar-brand { color: #fff; } .navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { color: #fff; } .navbar-dark .navbar-nav .nav-link { color: rgba(255, 255, 255, 0.55); } .navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { color: rgba(255, 255, 255, 0.75); } .navbar-dark .navbar-nav .nav-link.disabled { color: rgba(255, 255, 255, 0.25); } .navbar-dark .navbar-nav .show > .nav-link, .navbar-dark .navbar-nav .nav-link.active { color: #fff; } .navbar-dark .navbar-toggler { color: rgba(255, 255, 255, 0.55); border-color: rgba(255, 255, 255, 0.1); } .navbar-dark .navbar-toggler-icon { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .navbar-dark .navbar-text { color: rgba(255, 255, 255, 0.55); } .navbar-dark .navbar-text a, .navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { color: #fff; } .card { position: relative; display: flex; flex-direction: column; min-width: 0; word-wrap: break-word; background-color: #fff; background-clip: border-box; border: 1px solid rgba(0, 0, 0, 0.125); border-radius: 0.25rem; } .card > hr { margin-right: 0; margin-left: 0; } .card > .list-group { border-top: inherit; border-bottom: inherit; } .card > .list-group:first-child { border-top-width: 0; border-top-left-radius: calc(0.25rem - 1px); border-top-right-radius: calc(0.25rem - 1px); } .card > .list-group:last-child { border-bottom-width: 0; border-bottom-right-radius: calc(0.25rem - 1px); border-bottom-left-radius: calc(0.25rem - 1px); } .card > .card-header + .list-group, .card > .list-group + .card-footer { border-top: 0; } .card-body { flex: 1 1 auto; padding: 1rem 1rem; } .card-title { margin-bottom: 0.5rem; } .card-subtitle { margin-top: -0.25rem; margin-bottom: 0; } .card-text:last-child { margin-bottom: 0; } .card-link + .card-link { margin-left: 1rem; } .card-header { padding: 0.5rem 1rem; margin-bottom: 0; background-color: rgba(0, 0, 0, 0.03); border-bottom: 1px solid rgba(0, 0, 0, 0.125); } .card-header:first-child { border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; } .card-footer { padding: 0.5rem 1rem; background-color: rgba(0, 0, 0, 0.03); border-top: 1px solid rgba(0, 0, 0, 0.125); } .card-footer:last-child { border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); } .card-header-tabs { margin-right: -0.5rem; margin-bottom: -0.5rem; margin-left: -0.5rem; border-bottom: 0; } .card-header-pills { margin-right: -0.5rem; margin-left: -0.5rem; } .card-img-overlay { position: absolute; top: 0; right: 0; bottom: 0; left: 0; padding: 1rem; border-radius: calc(0.25rem - 1px); } .card-img, .card-img-top, .card-img-bottom { width: 100%; } .card-img, .card-img-top { border-top-left-radius: calc(0.25rem - 1px); border-top-right-radius: calc(0.25rem - 1px); } .card-img, .card-img-bottom { border-bottom-right-radius: calc(0.25rem - 1px); border-bottom-left-radius: calc(0.25rem - 1px); } .card-group > .card { margin-bottom: 0.75rem; } @media (min-width: 576px) { .card-group { display: flex; flex-flow: row wrap; } .card-group > .card { flex: 1 0 0%; margin-bottom: 0; } .card-group > .card + .card { margin-left: 0; border-left: 0; } .card-group > .card:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .card-group > .card:not(:last-child) .card-img-top, .card-group > .card:not(:last-child) .card-header { border-top-right-radius: 0; } .card-group > .card:not(:last-child) .card-img-bottom, .card-group > .card:not(:last-child) .card-footer { border-bottom-right-radius: 0; } .card-group > .card:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; } .card-group > .card:not(:first-child) .card-img-top, .card-group > .card:not(:first-child) .card-header { border-top-left-radius: 0; } .card-group > .card:not(:first-child) .card-img-bottom, .card-group > .card:not(:first-child) .card-footer { border-bottom-left-radius: 0; } } .accordion-button { position: relative; display: flex; align-items: center; width: 100%; padding: 1rem 1.25rem; font-size: 1rem; color: #212529; text-align: left; background-color: #fff; border: 0; border-radius: 0; overflow-anchor: none; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease; } @media (prefers-reduced-motion: reduce) { .accordion-button { transition: none; } } .accordion-button:not(.collapsed) { color: #0c63e4; background-color: #e7f1ff; box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.125); } .accordion-button:not(.collapsed)::after { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); transform: rotate(-180deg); } .accordion-button::after { flex-shrink: 0; width: 1.25rem; height: 1.25rem; margin-left: auto; content: ""; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-size: 1.25rem; transition: transform 0.2s ease-in-out; } @media (prefers-reduced-motion: reduce) { .accordion-button::after { transition: none; } } .accordion-button:hover { z-index: 2; } .accordion-button:focus { z-index: 3; border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .accordion-header { margin-bottom: 0; } .accordion-item { background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.125); } .accordion-item:first-of-type { border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; } .accordion-item:first-of-type .accordion-button { border-top-left-radius: calc(0.25rem - 1px); border-top-right-radius: calc(0.25rem - 1px); } .accordion-item:not(:first-of-type) { border-top: 0; } .accordion-item:last-of-type { border-bottom-right-radius: 0.25rem; border-bottom-left-radius: 0.25rem; } .accordion-item:last-of-type .accordion-button.collapsed { border-bottom-right-radius: calc(0.25rem - 1px); border-bottom-left-radius: calc(0.25rem - 1px); } .accordion-item:last-of-type .accordion-collapse { border-bottom-right-radius: 0.25rem; border-bottom-left-radius: 0.25rem; } .accordion-body { padding: 1rem 1.25rem; } .accordion-flush .accordion-collapse { border-width: 0; } .accordion-flush .accordion-item { border-right: 0; border-left: 0; border-radius: 0; } .accordion-flush .accordion-item:first-child { border-top: 0; } .accordion-flush .accordion-item:last-child { border-bottom: 0; } .accordion-flush .accordion-item .accordion-button { border-radius: 0; } .breadcrumb { display: flex; flex-wrap: wrap; padding: 0 0; margin-bottom: 1rem; list-style: none; } .breadcrumb-item + .breadcrumb-item { padding-left: 0.5rem; } .breadcrumb-item + .breadcrumb-item::before { float: left; padding-right: 0.5rem; color: #6c757d; content: var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */; } .breadcrumb-item.active { color: #6c757d; } .pagination { display: flex; padding-left: 0; list-style: none; } .page-link { position: relative; display: block; color: #0d6efd; text-decoration: none; background-color: #fff; border: 1px solid #dee2e6; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .page-link { transition: none; } } .page-link:hover { z-index: 2; color: #0a58ca; background-color: #e9ecef; border-color: #dee2e6; } .page-link:focus { z-index: 3; color: #0a58ca; background-color: #e9ecef; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .page-item:not(:first-child) .page-link { margin-left: -1px; } .page-item.active .page-link { z-index: 3; color: #fff; background-color: #0d6efd; border-color: #0d6efd; } .page-item.disabled .page-link { color: #6c757d; pointer-events: none; background-color: #fff; border-color: #dee2e6; } .page-link { padding: 0.375rem 0.75rem; } .page-item:first-child .page-link { border-top-left-radius: 0.25rem; border-bottom-left-radius: 0.25rem; } .page-item:last-child .page-link { border-top-right-radius: 0.25rem; border-bottom-right-radius: 0.25rem; } .pagination-lg .page-link { padding: 0.75rem 1.5rem; font-size: 1.25rem; } .pagination-lg .page-item:first-child .page-link { border-top-left-radius: 0.3rem; border-bottom-left-radius: 0.3rem; } .pagination-lg .page-item:last-child .page-link { border-top-right-radius: 0.3rem; border-bottom-right-radius: 0.3rem; } .pagination-sm .page-link { padding: 0.25rem 0.5rem; font-size: 0.875rem; } .pagination-sm .page-item:first-child .page-link { border-top-left-radius: 0.2rem; border-bottom-left-radius: 0.2rem; } .pagination-sm .page-item:last-child .page-link { border-top-right-radius: 0.2rem; border-bottom-right-radius: 0.2rem; } .badge { display: inline-block; padding: 0.35em 0.65em; font-size: 0.75em; font-weight: 700; line-height: 1; color: #fff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: 0.25rem; } .badge:empty { display: none; } .btn .badge { position: relative; top: -1px; } .alert { position: relative; padding: 1rem 1rem; margin-bottom: 1rem; border: 1px solid transparent; border-radius: 0.25rem; } .alert-heading { color: inherit; } .alert-link { font-weight: 700; } .alert-dismissible { padding-right: 3rem; } .alert-dismissible .btn-close { position: absolute; top: 0; right: 0; z-index: 2; padding: 1.25rem 1rem; } .alert-primary { color: #084298; background-color: #cfe2ff; border-color: #b6d4fe; } .alert-primary .alert-link { color: #06357a; } .alert-secondary { color: #41464b; background-color: #e2e3e5; border-color: #d3d6d8; } .alert-secondary .alert-link { color: #34383c; } .alert-success { color: #0f5132; background-color: #d1e7dd; border-color: #badbcc; } .alert-success .alert-link { color: #0c4128; } .alert-info { color: #055160; background-color: #cff4fc; border-color: #b6effb; } .alert-info .alert-link { color: #04414d; } .alert-warning { color: #664d03; background-color: #fff3cd; border-color: #ffecb5; } .alert-warning .alert-link { color: #523e02; } .alert-danger { color: #842029; background-color: #f8d7da; border-color: #f5c2c7; } .alert-danger .alert-link { color: #6a1a21; } .alert-light { color: #636464; background-color: #fefefe; border-color: #fdfdfe; } .alert-light .alert-link { color: #4f5050; } .alert-dark { color: #141619; background-color: #d3d3d4; border-color: #bcbebf; } .alert-dark .alert-link { color: #101214; } @-webkit-keyframes progress-bar-stripes { 0% { background-position-x: 1rem; } } @keyframes progress-bar-stripes { 0% { background-position-x: 1rem; } } .progress { display: flex; height: 1rem; overflow: hidden; font-size: 0.75rem; background-color: #e9ecef; border-radius: 0.25rem; } .progress-bar { display: flex; flex-direction: column; justify-content: center; overflow: hidden; color: #fff; text-align: center; white-space: nowrap; background-color: #0d6efd; transition: width 0.6s ease; } @media (prefers-reduced-motion: reduce) { .progress-bar { transition: none; } } .progress-bar-striped { background-image: linear-gradient( 45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent ); background-size: 1rem 1rem; } .progress-bar-animated { -webkit-animation: 1s linear infinite progress-bar-stripes; animation: 1s linear infinite progress-bar-stripes; } @media (prefers-reduced-motion: reduce) { .progress-bar-animated { -webkit-animation: none; animation: none; } } .list-group { display: flex; flex-direction: column; padding-left: 0; margin-bottom: 0; border-radius: 0.25rem; } .list-group-numbered { list-style-type: none; counter-reset: section; } .list-group-numbered > li::before { content: counters(section, ".") ". "; counter-increment: section; } .list-group-item-action { width: 100%; color: #495057; text-align: inherit; } .list-group-item-action:hover, .list-group-item-action:focus { z-index: 1; color: #495057; text-decoration: none; background-color: #f8f9fa; } .list-group-item-action:active { color: #212529; background-color: #e9ecef; } .list-group-item { position: relative; display: block; padding: 0.5rem 1rem; color: #212529; text-decoration: none; background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.125); } .list-group-item:first-child { border-top-left-radius: inherit; border-top-right-radius: inherit; } .list-group-item:last-child { border-bottom-right-radius: inherit; border-bottom-left-radius: inherit; } .list-group-item.disabled, .list-group-item:disabled { color: #6c757d; pointer-events: none; background-color: #fff; } .list-group-item.active { z-index: 2; color: #fff; background-color: #0d6efd; border-color: #0d6efd; } .list-group-item + .list-group-item { border-top-width: 0; } .list-group-item + .list-group-item.active { margin-top: -1px; border-top-width: 1px; } .list-group-horizontal { flex-direction: row; } .list-group-horizontal > .list-group-item:first-child { border-bottom-left-radius: 0.25rem; border-top-right-radius: 0; } .list-group-horizontal > .list-group-item:last-child { border-top-right-radius: 0.25rem; border-bottom-left-radius: 0; } .list-group-horizontal > .list-group-item.active { margin-top: 0; } .list-group-horizontal > .list-group-item + .list-group-item { border-top-width: 1px; border-left-width: 0; } .list-group-horizontal > .list-group-item + .list-group-item.active { margin-left: -1px; border-left-width: 1px; } @media (min-width: 576px) { .list-group-horizontal-sm { flex-direction: row; } .list-group-horizontal-sm > .list-group-item:first-child { border-bottom-left-radius: 0.25rem; border-top-right-radius: 0; } .list-group-horizontal-sm > .list-group-item:last-child { border-top-right-radius: 0.25rem; border-bottom-left-radius: 0; } .list-group-horizontal-sm > .list-group-item.active { margin-top: 0; } .list-group-horizontal-sm > .list-group-item + .list-group-item { border-top-width: 1px; border-left-width: 0; } .list-group-horizontal-sm > .list-group-item + .list-group-item.active { margin-left: -1px; border-left-width: 1px; } } @media (min-width: 768px) { .list-group-horizontal-md { flex-direction: row; } .list-group-horizontal-md > .list-group-item:first-child { border-bottom-left-radius: 0.25rem; border-top-right-radius: 0; } .list-group-horizontal-md > .list-group-item:last-child { border-top-right-radius: 0.25rem; border-bottom-left-radius: 0; } .list-group-horizontal-md > .list-group-item.active { margin-top: 0; } .list-group-horizontal-md > .list-group-item + .list-group-item { border-top-width: 1px; border-left-width: 0; } .list-group-horizontal-md > .list-group-item + .list-group-item.active { margin-left: -1px; border-left-width: 1px; } } @media (min-width: 992px) { .list-group-horizontal-lg { flex-direction: row; } .list-group-horizontal-lg > .list-group-item:first-child { border-bottom-left-radius: 0.25rem; border-top-right-radius: 0; } .list-group-horizontal-lg > .list-group-item:last-child { border-top-right-radius: 0.25rem; border-bottom-left-radius: 0; } .list-group-horizontal-lg > .list-group-item.active { margin-top: 0; } .list-group-horizontal-lg > .list-group-item + .list-group-item { border-top-width: 1px; border-left-width: 0; } .list-group-horizontal-lg > .list-group-item + .list-group-item.active { margin-left: -1px; border-left-width: 1px; } } @media (min-width: 1200px) { .list-group-horizontal-xl { flex-direction: row; } .list-group-horizontal-xl > .list-group-item:first-child { border-bottom-left-radius: 0.25rem; border-top-right-radius: 0; } .list-group-horizontal-xl > .list-group-item:last-child { border-top-right-radius: 0.25rem; border-bottom-left-radius: 0; } .list-group-horizontal-xl > .list-group-item.active { margin-top: 0; } .list-group-horizontal-xl > .list-group-item + .list-group-item { border-top-width: 1px; border-left-width: 0; } .list-group-horizontal-xl > .list-group-item + .list-group-item.active { margin-left: -1px; border-left-width: 1px; } } @media (min-width: 1400px) { .list-group-horizontal-xxl { flex-direction: row; } .list-group-horizontal-xxl > .list-group-item:first-child { border-bottom-left-radius: 0.25rem; border-top-right-radius: 0; } .list-group-horizontal-xxl > .list-group-item:last-child { border-top-right-radius: 0.25rem; border-bottom-left-radius: 0; } .list-group-horizontal-xxl > .list-group-item.active { margin-top: 0; } .list-group-horizontal-xxl > .list-group-item + .list-group-item { border-top-width: 1px; border-left-width: 0; } .list-group-horizontal-xxl > .list-group-item + .list-group-item.active { margin-left: -1px; border-left-width: 1px; } } .list-group-flush { border-radius: 0; } .list-group-flush > .list-group-item { border-width: 0 0 1px; } .list-group-flush > .list-group-item:last-child { border-bottom-width: 0; } .list-group-item-primary { color: #084298; background-color: #cfe2ff; } .list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { color: #084298; background-color: #bacbe6; } .list-group-item-primary.list-group-item-action.active { color: #fff; background-color: #084298; border-color: #084298; } .list-group-item-secondary { color: #41464b; background-color: #e2e3e5; } .list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { color: #41464b; background-color: #cbccce; } .list-group-item-secondary.list-group-item-action.active { color: #fff; background-color: #41464b; border-color: #41464b; } .list-group-item-success { color: #0f5132; background-color: #d1e7dd; } .list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { color: #0f5132; background-color: #bcd0c7; } .list-group-item-success.list-group-item-action.active { color: #fff; background-color: #0f5132; border-color: #0f5132; } .list-group-item-info { color: #055160; background-color: #cff4fc; } .list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { color: #055160; background-color: #badce3; } .list-group-item-info.list-group-item-action.active { color: #fff; background-color: #055160; border-color: #055160; } .list-group-item-warning { color: #664d03; background-color: #fff3cd; } .list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { color: #664d03; background-color: #e6dbb9; } .list-group-item-warning.list-group-item-action.active { color: #fff; background-color: #664d03; border-color: #664d03; } .list-group-item-danger { color: #842029; background-color: #f8d7da; } .list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { color: #842029; background-color: #dfc2c4; } .list-group-item-danger.list-group-item-action.active { color: #fff; background-color: #842029; border-color: #842029; } .list-group-item-light { color: #636464; background-color: #fefefe; } .list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { color: #636464; background-color: #e5e5e5; } .list-group-item-light.list-group-item-action.active { color: #fff; background-color: #636464; border-color: #636464; } .list-group-item-dark { color: #141619; background-color: #d3d3d4; } .list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { color: #141619; background-color: #bebebf; } .list-group-item-dark.list-group-item-action.active { color: #fff; background-color: #141619; border-color: #141619; } .btn-close { box-sizing: content-box; width: 1em; height: 1em; padding: 0.25em 0.25em; color: #000; background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat; border: 0; border-radius: 0.25rem; opacity: 0.5; } .btn-close:hover { color: #000; text-decoration: none; opacity: 0.75; } .btn-close:focus { outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); opacity: 1; } .btn-close:disabled, .btn-close.disabled { pointer-events: none; -webkit-user-select: none; -moz-user-select: none; user-select: none; opacity: 0.25; } .btn-close-white { filter: invert(1) grayscale(100%) brightness(200%); } .toast { width: 350px; max-width: 100%; font-size: 0.875rem; pointer-events: auto; background-color: rgba(255, 255, 255, 0.85); background-clip: padding-box; border: 1px solid rgba(0, 0, 0, 0.1); box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); border-radius: 0.25rem; } .toast.showing { opacity: 0; } .toast:not(.show) { display: none; } .toast-container { width: -webkit-max-content; width: -moz-max-content; width: max-content; max-width: 100%; pointer-events: none; } .toast-container > :not(:last-child) { margin-bottom: 0.75rem; } .toast-header { display: flex; align-items: center; padding: 0.5rem 0.75rem; color: #6c757d; background-color: rgba(255, 255, 255, 0.85); background-clip: padding-box; border-bottom: 1px solid rgba(0, 0, 0, 0.05); border-top-left-radius: calc(0.25rem - 1px); border-top-right-radius: calc(0.25rem - 1px); } .toast-header .btn-close { margin-right: -0.375rem; margin-left: 0.75rem; } .toast-body { padding: 0.75rem; word-wrap: break-word; } .modal { position: fixed; top: 0; left: 0; z-index: 1055; display: none; width: 100%; height: 100%; overflow-x: hidden; overflow-y: auto; outline: 0; } .modal-dialog { position: relative; width: auto; margin: 0.5rem; pointer-events: none; } .modal.fade .modal-dialog { transition: transform 0.3s ease-out; transform: translate(0, -50px); } @media (prefers-reduced-motion: reduce) { .modal.fade .modal-dialog { transition: none; } } .modal.show .modal-dialog { transform: none; } .modal.modal-static .modal-dialog { transform: scale(1.02); } .modal-dialog-scrollable { height: calc(100% - 1rem); } .modal-dialog-scrollable .modal-content { max-height: 100%; overflow: hidden; } .modal-dialog-scrollable .modal-body { overflow-y: auto; } .modal-dialog-centered { display: flex; align-items: center; min-height: calc(100% - 1rem); } .modal-content { position: relative; display: flex; flex-direction: column; width: 100%; pointer-events: auto; background-color: #fff; background-clip: padding-box; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 0.3rem; outline: 0; } .modal-backdrop { position: fixed; top: 0; left: 0; z-index: 1050; width: 100vw; height: 100vh; background-color: #000; } .modal-backdrop.fade { opacity: 0; } .modal-backdrop.show { opacity: 0.5; } .modal-header { display: flex; flex-shrink: 0; align-items: center; justify-content: space-between; padding: 1rem 1rem; border-bottom: 1px solid #dee2e6; border-top-left-radius: calc(0.3rem - 1px); border-top-right-radius: calc(0.3rem - 1px); } .modal-header .btn-close { padding: 0.5rem 0.5rem; margin: -0.5rem -0.5rem -0.5rem auto; } .modal-title { margin-bottom: 0; line-height: 1.5; } .modal-body { position: relative; flex: 1 1 auto; padding: 1rem; } .modal-footer { display: flex; flex-wrap: wrap; flex-shrink: 0; align-items: center; justify-content: flex-end; padding: 0.75rem; border-top: 1px solid #dee2e6; border-bottom-right-radius: calc(0.3rem - 1px); border-bottom-left-radius: calc(0.3rem - 1px); } .modal-footer > * { margin: 0.25rem; } @media (min-width: 576px) { .modal-dialog { max-width: 500px; margin: 1.75rem auto; } .modal-dialog-scrollable { height: calc(100% - 3.5rem); } .modal-dialog-centered { min-height: calc(100% - 3.5rem); } .modal-sm { max-width: 300px; } } @media (min-width: 992px) { .modal-lg, .modal-xl { max-width: 800px; } } @media (min-width: 1200px) { .modal-xl { max-width: 1140px; } } .modal-fullscreen { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen .modal-header { border-radius: 0; } .modal-fullscreen .modal-body { overflow-y: auto; } .modal-fullscreen .modal-footer { border-radius: 0; } @media (max-width: 575.98px) { .modal-fullscreen-sm-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-sm-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-sm-down .modal-header { border-radius: 0; } .modal-fullscreen-sm-down .modal-body { overflow-y: auto; } .modal-fullscreen-sm-down .modal-footer { border-radius: 0; } } @media (max-width: 767.98px) { .modal-fullscreen-md-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-md-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-md-down .modal-header { border-radius: 0; } .modal-fullscreen-md-down .modal-body { overflow-y: auto; } .modal-fullscreen-md-down .modal-footer { border-radius: 0; } } @media (max-width: 991.98px) { .modal-fullscreen-lg-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-lg-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-lg-down .modal-header { border-radius: 0; } .modal-fullscreen-lg-down .modal-body { overflow-y: auto; } .modal-fullscreen-lg-down .modal-footer { border-radius: 0; } } @media (max-width: 1199.98px) { .modal-fullscreen-xl-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-xl-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-xl-down .modal-header { border-radius: 0; } .modal-fullscreen-xl-down .modal-body { overflow-y: auto; } .modal-fullscreen-xl-down .modal-footer { border-radius: 0; } } @media (max-width: 1399.98px) { .modal-fullscreen-xxl-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-xxl-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-xxl-down .modal-header { border-radius: 0; } .modal-fullscreen-xxl-down .modal-body { overflow-y: auto; } .modal-fullscreen-xxl-down .modal-footer { border-radius: 0; } } .tooltip { position: absolute; z-index: 1080; display: block; margin: 0; font-family: var(--bs-font-sans-serif); font-style: normal; font-weight: 400; line-height: 1.5; text-align: left; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; letter-spacing: normal; word-break: normal; word-spacing: normal; white-space: normal; line-break: auto; font-size: 0.875rem; word-wrap: break-word; opacity: 0; } .tooltip.show { opacity: 0.9; } .tooltip .tooltip-arrow { position: absolute; display: block; width: 0.8rem; height: 0.4rem; } .tooltip .tooltip-arrow::before { position: absolute; content: ""; border-color: transparent; border-style: solid; } .bs-tooltip-top, .bs-tooltip-auto[data-popper-placement^="top"] { padding: 0.4rem 0; } .bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow { bottom: 0; } .bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow::before { top: -1px; border-width: 0.4rem 0.4rem 0; border-top-color: #000; } .bs-tooltip-end, .bs-tooltip-auto[data-popper-placement^="right"] { padding: 0 0.4rem; } .bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow { left: 0; width: 0.4rem; height: 0.8rem; } .bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow::before { right: -1px; border-width: 0.4rem 0.4rem 0.4rem 0; border-right-color: #000; } .bs-tooltip-bottom, .bs-tooltip-auto[data-popper-placement^="bottom"] { padding: 0.4rem 0; } .bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow { top: 0; } .bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow::before { bottom: -1px; border-width: 0 0.4rem 0.4rem; border-bottom-color: #000; } .bs-tooltip-start, .bs-tooltip-auto[data-popper-placement^="left"] { padding: 0 0.4rem; } .bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow { right: 0; width: 0.4rem; height: 0.8rem; } .bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow::before { left: -1px; border-width: 0.4rem 0 0.4rem 0.4rem; border-left-color: #000; } .tooltip-inner { max-width: 200px; padding: 0.25rem 0.5rem; color: #fff; text-align: center; background-color: #000; border-radius: 0.25rem; } .popover { position: absolute; top: 0; left: 0 /* rtl:ignore */; z-index: 1070; display: block; max-width: 276px; font-family: var(--bs-font-sans-serif); font-style: normal; font-weight: 400; line-height: 1.5; text-align: left; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; letter-spacing: normal; word-break: normal; word-spacing: normal; white-space: normal; line-break: auto; font-size: 0.875rem; word-wrap: break-word; background-color: #fff; background-clip: padding-box; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 0.3rem; } .popover .popover-arrow { position: absolute; display: block; width: 1rem; height: 0.5rem; } .popover .popover-arrow::before, .popover .popover-arrow::after { position: absolute; display: block; content: ""; border-color: transparent; border-style: solid; } .bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^="top"] > .popover-arrow { bottom: calc(-0.5rem - 1px); } .bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^="top"] > .popover-arrow::before { bottom: 0; border-width: 0.5rem 0.5rem 0; border-top-color: rgba(0, 0, 0, 0.25); } .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="top"] > .popover-arrow::after { bottom: 1px; border-width: 0.5rem 0.5rem 0; border-top-color: #fff; } .bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^="right"] > .popover-arrow { left: calc(-0.5rem - 1px); width: 0.5rem; height: 1rem; } .bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^="right"] > .popover-arrow::before { left: 0; border-width: 0.5rem 0.5rem 0.5rem 0; border-right-color: rgba(0, 0, 0, 0.25); } .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="right"] > .popover-arrow::after { left: 1px; border-width: 0.5rem 0.5rem 0.5rem 0; border-right-color: #fff; } .bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow { top: calc(-0.5rem - 1px); } .bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow::before { top: 0; border-width: 0 0.5rem 0.5rem 0.5rem; border-bottom-color: rgba(0, 0, 0, 0.25); } .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow::after { top: 1px; border-width: 0 0.5rem 0.5rem 0.5rem; border-bottom-color: #fff; } .bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^="bottom"] .popover-header::before { position: absolute; top: 0; left: 50%; display: block; width: 1rem; margin-left: -0.5rem; content: ""; border-bottom: 1px solid #f0f0f0; } .bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^="left"] > .popover-arrow { right: calc(-0.5rem - 1px); width: 0.5rem; height: 1rem; } .bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^="left"] > .popover-arrow::before { right: 0; border-width: 0.5rem 0 0.5rem 0.5rem; border-left-color: rgba(0, 0, 0, 0.25); } .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="left"] > .popover-arrow::after { right: 1px; border-width: 0.5rem 0 0.5rem 0.5rem; border-left-color: #fff; } .popover-header { padding: 0.5rem 1rem; margin-bottom: 0; font-size: 1rem; background-color: #f0f0f0; border-bottom: 1px solid rgba(0, 0, 0, 0.2); border-top-left-radius: calc(0.3rem - 1px); border-top-right-radius: calc(0.3rem - 1px); } .popover-header:empty { display: none; } .popover-body { padding: 1rem 1rem; color: #212529; } .carousel { position: relative; } .carousel.pointer-event { touch-action: pan-y; } .carousel-inner { position: relative; width: 100%; overflow: hidden; } .carousel-inner::after { display: block; clear: both; content: ""; } .carousel-item { position: relative; display: none; float: left; width: 100%; margin-right: -100%; -webkit-backface-visibility: hidden; backface-visibility: hidden; transition: transform 0.6s ease-in-out; } @media (prefers-reduced-motion: reduce) { .carousel-item { transition: none; } } .carousel-item.active, .carousel-item-next, .carousel-item-prev { display: block; } /* rtl:begin:ignore */ .carousel-item-next:not(.carousel-item-start), .active.carousel-item-end { transform: translateX(100%); } .carousel-item-prev:not(.carousel-item-end), .active.carousel-item-start { transform: translateX(-100%); } /* rtl:end:ignore */ .carousel-fade .carousel-item { opacity: 0; transition-property: opacity; transform: none; } .carousel-fade .carousel-item.active, .carousel-fade .carousel-item-next.carousel-item-start, .carousel-fade .carousel-item-prev.carousel-item-end { z-index: 1; opacity: 1; } .carousel-fade .active.carousel-item-start, .carousel-fade .active.carousel-item-end { z-index: 0; opacity: 0; transition: opacity 0s 0.6s; } @media (prefers-reduced-motion: reduce) { .carousel-fade .active.carousel-item-start, .carousel-fade .active.carousel-item-end { transition: none; } } .carousel-control-prev, .carousel-control-next { position: absolute; top: 0; bottom: 0; z-index: 1; display: flex; align-items: center; justify-content: center; width: 15%; padding: 0; color: #fff; text-align: center; background: none; border: 0; opacity: 0.5; transition: opacity 0.15s ease; } @media (prefers-reduced-motion: reduce) { .carousel-control-prev, .carousel-control-next { transition: none; } } .carousel-control-prev:hover, .carousel-control-prev:focus, .carousel-control-next:hover, .carousel-control-next:focus { color: #fff; text-decoration: none; outline: 0; opacity: 0.9; } .carousel-control-prev { left: 0; } .carousel-control-next { right: 0; } .carousel-control-prev-icon, .carousel-control-next-icon { display: inline-block; width: 2rem; height: 2rem; background-repeat: no-repeat; background-position: 50%; background-size: 100% 100%; } /* rtl:options: { "autoRename": true, "stringMap":[ { "name" : "prev-next", "search" : "prev", "replace" : "next" } ] } */ .carousel-control-prev-icon { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e"); } .carousel-control-next-icon { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); } .carousel-indicators { position: absolute; right: 0; bottom: 0; left: 0; z-index: 2; display: flex; justify-content: center; padding: 0; margin-right: 15%; margin-bottom: 1rem; margin-left: 15%; list-style: none; } .carousel-indicators [data-bs-target] { box-sizing: content-box; flex: 0 1 auto; width: 30px; height: 3px; padding: 0; margin-right: 3px; margin-left: 3px; text-indent: -999px; cursor: pointer; background-color: #fff; background-clip: padding-box; border: 0; border-top: 10px solid transparent; border-bottom: 10px solid transparent; opacity: 0.5; transition: opacity 0.6s ease; } @media (prefers-reduced-motion: reduce) { .carousel-indicators [data-bs-target] { transition: none; } } .carousel-indicators .active { opacity: 1; } .carousel-caption { position: absolute; right: 15%; bottom: 1.25rem; left: 15%; padding-top: 1.25rem; padding-bottom: 1.25rem; color: #fff; text-align: center; } .carousel-dark .carousel-control-prev-icon, .carousel-dark .carousel-control-next-icon { filter: invert(1) grayscale(100); } .carousel-dark .carousel-indicators [data-bs-target] { background-color: #000; } .carousel-dark .carousel-caption { color: #000; } @-webkit-keyframes spinner-border { to { transform: rotate(360deg) /* rtl:ignore */; } } @keyframes spinner-border { to { transform: rotate(360deg) /* rtl:ignore */; } } .spinner-border { display: inline-block; width: 2rem; height: 2rem; vertical-align: -0.125em; border: 0.25em solid currentColor; border-right-color: transparent; border-radius: 50%; -webkit-animation: 0.75s linear infinite spinner-border; animation: 0.75s linear infinite spinner-border; } .spinner-border-sm { width: 1rem; height: 1rem; border-width: 0.2em; } @-webkit-keyframes spinner-grow { 0% { transform: scale(0); } 50% { opacity: 1; transform: none; } } @keyframes spinner-grow { 0% { transform: scale(0); } 50% { opacity: 1; transform: none; } } .spinner-grow { display: inline-block; width: 2rem; height: 2rem; vertical-align: -0.125em; background-color: currentColor; border-radius: 50%; opacity: 0; -webkit-animation: 0.75s linear infinite spinner-grow; animation: 0.75s linear infinite spinner-grow; } .spinner-grow-sm { width: 1rem; height: 1rem; } @media (prefers-reduced-motion: reduce) { .spinner-border, .spinner-grow { -webkit-animation-duration: 1.5s; animation-duration: 1.5s; } } .offcanvas { position: fixed; bottom: 0; z-index: 1045; display: flex; flex-direction: column; max-width: 100%; visibility: hidden; background-color: #fff; background-clip: padding-box; outline: 0; transition: transform 0.3s ease-in-out; } @media (prefers-reduced-motion: reduce) { .offcanvas { transition: none; } } .offcanvas-backdrop { position: fixed; top: 0; left: 0; z-index: 1040; width: 100vw; height: 100vh; background-color: #000; } .offcanvas-backdrop.fade { opacity: 0; } .offcanvas-backdrop.show { opacity: 0.5; } .offcanvas-header { display: flex; align-items: center; justify-content: space-between; padding: 1rem 1rem; } .offcanvas-header .btn-close { padding: 0.5rem 0.5rem; margin-top: -0.5rem; margin-right: -0.5rem; margin-bottom: -0.5rem; } .offcanvas-title { margin-bottom: 0; line-height: 1.5; } .offcanvas-body { flex-grow: 1; padding: 1rem 1rem; overflow-y: auto; } .offcanvas-start { top: 0; left: 0; width: 400px; border-right: 1px solid rgba(0, 0, 0, 0.2); transform: translateX(-100%); } .offcanvas-end { top: 0; right: 0; width: 400px; border-left: 1px solid rgba(0, 0, 0, 0.2); transform: translateX(100%); } .offcanvas-top { top: 0; right: 0; left: 0; height: 30vh; max-height: 100%; border-bottom: 1px solid rgba(0, 0, 0, 0.2); transform: translateY(-100%); } .offcanvas-bottom { right: 0; left: 0; height: 30vh; max-height: 100%; border-top: 1px solid rgba(0, 0, 0, 0.2); transform: translateY(100%); } .offcanvas.show { transform: none; } .placeholder { display: inline-block; min-height: 1em; vertical-align: middle; cursor: wait; background-color: currentColor; opacity: 0.5; } .placeholder.btn::before { display: inline-block; content: ""; } .placeholder-xs { min-height: 0.6em; } .placeholder-sm { min-height: 0.8em; } .placeholder-lg { min-height: 1.2em; } .placeholder-glow .placeholder { -webkit-animation: placeholder-glow 2s ease-in-out infinite; animation: placeholder-glow 2s ease-in-out infinite; } @-webkit-keyframes placeholder-glow { 50% { opacity: 0.2; } } @keyframes placeholder-glow { 50% { opacity: 0.2; } } .placeholder-wave { -webkit-mask-image: linear-gradient( 130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95% ); mask-image: linear-gradient( 130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95% ); -webkit-mask-size: 200% 100%; mask-size: 200% 100%; -webkit-animation: placeholder-wave 2s linear infinite; animation: placeholder-wave 2s linear infinite; } @-webkit-keyframes placeholder-wave { 100% { -webkit-mask-position: -200% 0%; mask-position: -200% 0%; } } @keyframes placeholder-wave { 100% { -webkit-mask-position: -200% 0%; mask-position: -200% 0%; } } .clearfix::after { display: block; clear: both; content: ""; } .link-primary { color: #0d6efd; } .link-primary:hover, .link-primary:focus { color: #0a58ca; } .link-secondary { color: #6c757d; } .link-secondary:hover, .link-secondary:focus { color: #565e64; } .link-success { color: #198754; } .link-success:hover, .link-success:focus { color: #146c43; } .link-info { color: #0dcaf0; } .link-info:hover, .link-info:focus { color: #3dd5f3; } .link-warning { color: #ffc107; } .link-warning:hover, .link-warning:focus { color: #ffcd39; } .link-danger { color: #dc3545; } .link-danger:hover, .link-danger:focus { color: #b02a37; } .link-light { color: #f8f9fa; } .link-light:hover, .link-light:focus { color: #f9fafb; } .link-dark { color: #212529; } .link-dark:hover, .link-dark:focus { color: #1a1e21; } .ratio { position: relative; width: 100%; } .ratio::before { display: block; padding-top: var(--bs-aspect-ratio); content: ""; } .ratio > * { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .ratio-1x1 { --bs-aspect-ratio: 100%; } .ratio-4x3 { --bs-aspect-ratio: 75%; } .ratio-16x9 { --bs-aspect-ratio: 56.25%; } .ratio-21x9 { --bs-aspect-ratio: 42.8571428571%; } .fixed-top { position: fixed; top: 0; right: 0; left: 0; z-index: 1030; } .fixed-bottom { position: fixed; right: 0; bottom: 0; left: 0; z-index: 1030; } .sticky-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } @media (min-width: 576px) { .sticky-sm-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 768px) { .sticky-md-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 992px) { .sticky-lg-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 1200px) { .sticky-xl-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 1400px) { .sticky-xxl-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } .hstack { display: flex; flex-direction: row; align-items: center; align-self: stretch; } .vstack { display: flex; flex: 1 1 auto; flex-direction: column; align-self: stretch; } .visually-hidden, .visually-hidden-focusable:not(:focus):not(:focus-within) { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; border: 0 !important; } .stretched-link::after { position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 1; content: ""; } .text-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .vr { display: inline-block; align-self: stretch; width: 1px; min-height: 1em; background-color: currentColor; opacity: 0.25; } .align-baseline { vertical-align: baseline !important; } .align-top { vertical-align: top !important; } .align-middle { vertical-align: middle !important; } .align-bottom { vertical-align: bottom !important; } .align-text-bottom { vertical-align: text-bottom !important; } .align-text-top { vertical-align: text-top !important; } .float-start { float: left !important; } .float-end { float: right !important; } .float-none { float: none !important; } .opacity-0 { opacity: 0 !important; } .opacity-25 { opacity: 0.25 !important; } .opacity-50 { opacity: 0.5 !important; } .opacity-75 { opacity: 0.75 !important; } .opacity-100 { opacity: 1 !important; } .overflow-auto { overflow: auto !important; } .overflow-hidden { overflow: hidden !important; } .overflow-visible { overflow: visible !important; } .overflow-scroll { overflow: scroll !important; } .d-inline { display: inline !important; } .d-inline-block { display: inline-block !important; } .d-block { display: block !important; } .d-grid { display: grid !important; } .d-table { display: table !important; } .d-table-row { display: table-row !important; } .d-table-cell { display: table-cell !important; } .d-flex { display: flex !important; } .d-inline-flex { display: inline-flex !important; } .d-none { display: none !important; } .shadow { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; } .shadow-sm { box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; } .shadow-lg { box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; } .shadow-none { box-shadow: none !important; } .position-static { position: static !important; } .position-relative { position: relative !important; } .position-absolute { position: absolute !important; } .position-fixed { position: fixed !important; } .position-sticky { position: -webkit-sticky !important; position: sticky !important; } .top-0 { top: 0 !important; } .top-50 { top: 50% !important; } .top-100 { top: 100% !important; } .bottom-0 { bottom: 0 !important; } .bottom-50 { bottom: 50% !important; } .bottom-100 { bottom: 100% !important; } .start-0 { left: 0 !important; } .start-50 { left: 50% !important; } .start-100 { left: 100% !important; } .end-0 { right: 0 !important; } .end-50 { right: 50% !important; } .end-100 { right: 100% !important; } .translate-middle { transform: translate(-50%, -50%) !important; } .translate-middle-x { transform: translateX(-50%) !important; } .translate-middle-y { transform: translateY(-50%) !important; } .border { border: 1px solid #dee2e6 !important; } .border-0 { border: 0 !important; } .border-top { border-top: 1px solid #dee2e6 !important; } .border-top-0 { border-top: 0 !important; } .border-end { border-right: 1px solid #dee2e6 !important; } .border-end-0 { border-right: 0 !important; } .border-bottom { border-bottom: 1px solid #dee2e6 !important; } .border-bottom-0 { border-bottom: 0 !important; } .border-start { border-left: 1px solid #dee2e6 !important; } .border-start-0 { border-left: 0 !important; } .border-primary { border-color: #0d6efd !important; } .border-secondary { border-color: #6c757d !important; } .border-success { border-color: #198754 !important; } .border-info { border-color: #0dcaf0 !important; } .border-warning { border-color: #ffc107 !important; } .border-danger { border-color: #dc3545 !important; } .border-light { border-color: #f8f9fa !important; } .border-dark { border-color: #212529 !important; } .border-white { border-color: #fff !important; } .border-1 { border-width: 1px !important; } .border-2 { border-width: 2px !important; } .border-3 { border-width: 3px !important; } .border-4 { border-width: 4px !important; } .border-5 { border-width: 5px !important; } .w-25 { width: 25% !important; } .w-50 { width: 50% !important; } .w-75 { width: 75% !important; } .w-100 { width: 100% !important; } .w-auto { width: auto !important; } .mw-100 { max-width: 100% !important; } .vw-100 { width: 100vw !important; } .min-vw-100 { min-width: 100vw !important; } .h-25 { height: 25% !important; } .h-50 { height: 50% !important; } .h-75 { height: 75% !important; } .h-100 { height: 100% !important; } .h-auto { height: auto !important; } .mh-100 { max-height: 100% !important; } .vh-100 { height: 100vh !important; } .min-vh-100 { min-height: 100vh !important; } .flex-fill { flex: 1 1 auto !important; } .flex-row { flex-direction: row !important; } .flex-column { flex-direction: column !important; } .flex-row-reverse { flex-direction: row-reverse !important; } .flex-column-reverse { flex-direction: column-reverse !important; } .flex-grow-0 { flex-grow: 0 !important; } .flex-grow-1 { flex-grow: 1 !important; } .flex-shrink-0 { flex-shrink: 0 !important; } .flex-shrink-1 { flex-shrink: 1 !important; } .flex-wrap { flex-wrap: wrap !important; } .flex-nowrap { flex-wrap: nowrap !important; } .flex-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-0 { gap: 0 !important; } .gap-1 { gap: 0.25rem !important; } .gap-2 { gap: 0.5rem !important; } .gap-3 { gap: 1rem !important; } .gap-4 { gap: 1.5rem !important; } .gap-5 { gap: 3rem !important; } .justify-content-start { justify-content: flex-start !important; } .justify-content-end { justify-content: flex-end !important; } .justify-content-center { justify-content: center !important; } .justify-content-between { justify-content: space-between !important; } .justify-content-around { justify-content: space-around !important; } .justify-content-evenly { justify-content: space-evenly !important; } .align-items-start { align-items: flex-start !important; } .align-items-end { align-items: flex-end !important; } .align-items-center { align-items: center !important; } .align-items-baseline { align-items: baseline !important; } .align-items-stretch { align-items: stretch !important; } .align-content-start { align-content: flex-start !important; } .align-content-end { align-content: flex-end !important; } .align-content-center { align-content: center !important; } .align-content-between { align-content: space-between !important; } .align-content-around { align-content: space-around !important; } .align-content-stretch { align-content: stretch !important; } .align-self-auto { align-self: auto !important; } .align-self-start { align-self: flex-start !important; } .align-self-end { align-self: flex-end !important; } .align-self-center { align-self: center !important; } .align-self-baseline { align-self: baseline !important; } .align-self-stretch { align-self: stretch !important; } .order-first { order: -1 !important; } .order-0 { order: 0 !important; } .order-1 { order: 1 !important; } .order-2 { order: 2 !important; } .order-3 { order: 3 !important; } .order-4 { order: 4 !important; } .order-5 { order: 5 !important; } .order-last { order: 6 !important; } .m-0 { margin: 0 !important; } .m-1 { margin: 0.25rem !important; } .m-2 { margin: 0.5rem !important; } .m-3 { margin: 1rem !important; } .m-4 { margin: 1.5rem !important; } .m-5 { margin: 3rem !important; } .m-auto { margin: auto !important; } .mx-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-auto { margin-right: auto !important; margin-left: auto !important; } .my-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-0 { margin-top: 0 !important; } .mt-1 { margin-top: 0.25rem !important; } .mt-2 { margin-top: 0.5rem !important; } .mt-3 { margin-top: 1rem !important; } .mt-4 { margin-top: 1.5rem !important; } .mt-5 { margin-top: 3rem !important; } .mt-auto { margin-top: auto !important; } .me-0 { margin-right: 0 !important; } .me-1 { margin-right: 0.25rem !important; } .me-2 { margin-right: 0.5rem !important; } .me-3 { margin-right: 1rem !important; } .me-4 { margin-right: 1.5rem !important; } .me-5 { margin-right: 3rem !important; } .me-auto { margin-right: auto !important; } .mb-0 { margin-bottom: 0 !important; } .mb-1 { margin-bottom: 0.25rem !important; } .mb-2 { margin-bottom: 0.5rem !important; } .mb-3 { margin-bottom: 1rem !important; } .mb-4 { margin-bottom: 1.5rem !important; } .mb-5 { margin-bottom: 3rem !important; } .mb-auto { margin-bottom: auto !important; } .ms-0 { margin-left: 0 !important; } .ms-1 { margin-left: 0.25rem !important; } .ms-2 { margin-left: 0.5rem !important; } .ms-3 { margin-left: 1rem !important; } .ms-4 { margin-left: 1.5rem !important; } .ms-5 { margin-left: 3rem !important; } .ms-auto { margin-left: auto !important; } .p-0 { padding: 0 !important; } .p-1 { padding: 0.25rem !important; } .p-2 { padding: 0.5rem !important; } .p-3 { padding: 1rem !important; } .p-4 { padding: 1.5rem !important; } .p-5 { padding: 3rem !important; } .px-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-0 { padding-top: 0 !important; } .pt-1 { padding-top: 0.25rem !important; } .pt-2 { padding-top: 0.5rem !important; } .pt-3 { padding-top: 1rem !important; } .pt-4 { padding-top: 1.5rem !important; } .pt-5 { padding-top: 3rem !important; } .pe-0 { padding-right: 0 !important; } .pe-1 { padding-right: 0.25rem !important; } .pe-2 { padding-right: 0.5rem !important; } .pe-3 { padding-right: 1rem !important; } .pe-4 { padding-right: 1.5rem !important; } .pe-5 { padding-right: 3rem !important; } .pb-0 { padding-bottom: 0 !important; } .pb-1 { padding-bottom: 0.25rem !important; } .pb-2 { padding-bottom: 0.5rem !important; } .pb-3 { padding-bottom: 1rem !important; } .pb-4 { padding-bottom: 1.5rem !important; } .pb-5 { padding-bottom: 3rem !important; } .ps-0 { padding-left: 0 !important; } .ps-1 { padding-left: 0.25rem !important; } .ps-2 { padding-left: 0.5rem !important; } .ps-3 { padding-left: 1rem !important; } .ps-4 { padding-left: 1.5rem !important; } .ps-5 { padding-left: 3rem !important; } .font-monospace { font-family: var(--bs-font-monospace) !important; } .fs-1 { font-size: calc(1.375rem + 1.5vw) !important; } .fs-2 { font-size: calc(1.325rem + 0.9vw) !important; } .fs-3 { font-size: calc(1.3rem + 0.6vw) !important; } .fs-4 { font-size: calc(1.275rem + 0.3vw) !important; } .fs-5 { font-size: 1.25rem !important; } .fs-6 { font-size: 1rem !important; } .fst-italic { font-style: italic !important; } .fst-normal { font-style: normal !important; } .fw-light { font-weight: 300 !important; } .fw-lighter { font-weight: lighter !important; } .fw-normal { font-weight: 400 !important; } .fw-bold { font-weight: 700 !important; } .fw-bolder { font-weight: bolder !important; } .lh-1 { line-height: 1 !important; } .lh-sm { line-height: 1.25 !important; } .lh-base { line-height: 1.5 !important; } .lh-lg { line-height: 2 !important; } .text-start { text-align: left !important; } .text-end { text-align: right !important; } .text-center { text-align: center !important; } .text-decoration-none { text-decoration: none !important; } .text-decoration-underline { text-decoration: underline !important; } .text-decoration-line-through { text-decoration: line-through !important; } .text-lowercase { text-transform: lowercase !important; } .text-uppercase { text-transform: uppercase !important; } .text-capitalize { text-transform: capitalize !important; } .text-wrap { white-space: normal !important; } .text-nowrap { white-space: nowrap !important; } /* rtl:begin:remove */ .text-break { word-wrap: break-word !important; word-break: break-word !important; } /* rtl:end:remove */ .text-primary { --bs-text-opacity: 1; color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; } .text-secondary { --bs-text-opacity: 1; color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; } .text-success { --bs-text-opacity: 1; color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; } .text-info { --bs-text-opacity: 1; color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; } .text-warning { --bs-text-opacity: 1; color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; } .text-danger { --bs-text-opacity: 1; color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; } .text-light { --bs-text-opacity: 1; color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; } .text-dark { --bs-text-opacity: 1; color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; } .text-black { --bs-text-opacity: 1; color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; } .text-white { --bs-text-opacity: 1; color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; } .text-body { --bs-text-opacity: 1; color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; } .text-muted { --bs-text-opacity: 1; color: #6c757d !important; } .text-black-50 { --bs-text-opacity: 1; color: rgba(0, 0, 0, 0.5) !important; } .text-white-50 { --bs-text-opacity: 1; color: rgba(255, 255, 255, 0.5) !important; } .text-reset { --bs-text-opacity: 1; color: inherit !important; } .text-opacity-25 { --bs-text-opacity: 0.25; } .text-opacity-50 { --bs-text-opacity: 0.5; } .text-opacity-75 { --bs-text-opacity: 0.75; } .text-opacity-100 { --bs-text-opacity: 1; } .bg-primary { --bs-bg-opacity: 1; background-color: rgba( var(--bs-primary-rgb), var(--bs-bg-opacity) ) !important; } .bg-secondary { --bs-bg-opacity: 1; background-color: rgba( var(--bs-secondary-rgb), var(--bs-bg-opacity) ) !important; } .bg-success { --bs-bg-opacity: 1; background-color: rgba( var(--bs-success-rgb), var(--bs-bg-opacity) ) !important; } .bg-info { --bs-bg-opacity: 1; background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; } .bg-warning { --bs-bg-opacity: 1; background-color: rgba( var(--bs-warning-rgb), var(--bs-bg-opacity) ) !important; } .bg-danger { --bs-bg-opacity: 1; background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; } .bg-light { --bs-bg-opacity: 1; background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; } .bg-dark { --bs-bg-opacity: 1; background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } .bg-black { --bs-bg-opacity: 1; background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; } .bg-white { --bs-bg-opacity: 1; background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; } .bg-body { --bs-bg-opacity: 1; background-color: rgba( var(--bs-body-bg-rgb), var(--bs-bg-opacity) ) !important; } .bg-transparent { --bs-bg-opacity: 1; background-color: transparent !important; } .bg-opacity-10 { --bs-bg-opacity: 0.1; } .bg-opacity-25 { --bs-bg-opacity: 0.25; } .bg-opacity-50 { --bs-bg-opacity: 0.5; } .bg-opacity-75 { --bs-bg-opacity: 0.75; } .bg-opacity-100 { --bs-bg-opacity: 1; } .bg-gradient { background-image: var(--bs-gradient) !important; } .user-select-all { -webkit-user-select: all !important; -moz-user-select: all !important; user-select: all !important; } .user-select-auto { -webkit-user-select: auto !important; -moz-user-select: auto !important; user-select: auto !important; } .user-select-none { -webkit-user-select: none !important; -moz-user-select: none !important; user-select: none !important; } .pe-none { pointer-events: none !important; } .pe-auto { pointer-events: auto !important; } .rounded { border-radius: 0.25rem !important; } .rounded-0 { border-radius: 0 !important; } .rounded-1 { border-radius: 0.2rem !important; } .rounded-2 { border-radius: 0.25rem !important; } .rounded-3 { border-radius: 0.3rem !important; } .rounded-circle { border-radius: 50% !important; } .rounded-pill { border-radius: 50rem !important; } .rounded-top { border-top-left-radius: 0.25rem !important; border-top-right-radius: 0.25rem !important; } .rounded-end { border-top-right-radius: 0.25rem !important; border-bottom-right-radius: 0.25rem !important; } .rounded-bottom { border-bottom-right-radius: 0.25rem !important; border-bottom-left-radius: 0.25rem !important; } .rounded-start { border-bottom-left-radius: 0.25rem !important; border-top-left-radius: 0.25rem !important; } .visible { visibility: visible !important; } .invisible { visibility: hidden !important; } @media (min-width: 576px) { .float-sm-start { float: left !important; } .float-sm-end { float: right !important; } .float-sm-none { float: none !important; } .d-sm-inline { display: inline !important; } .d-sm-inline-block { display: inline-block !important; } .d-sm-block { display: block !important; } .d-sm-grid { display: grid !important; } .d-sm-table { display: table !important; } .d-sm-table-row { display: table-row !important; } .d-sm-table-cell { display: table-cell !important; } .d-sm-flex { display: flex !important; } .d-sm-inline-flex { display: inline-flex !important; } .d-sm-none { display: none !important; } .flex-sm-fill { flex: 1 1 auto !important; } .flex-sm-row { flex-direction: row !important; } .flex-sm-column { flex-direction: column !important; } .flex-sm-row-reverse { flex-direction: row-reverse !important; } .flex-sm-column-reverse { flex-direction: column-reverse !important; } .flex-sm-grow-0 { flex-grow: 0 !important; } .flex-sm-grow-1 { flex-grow: 1 !important; } .flex-sm-shrink-0 { flex-shrink: 0 !important; } .flex-sm-shrink-1 { flex-shrink: 1 !important; } .flex-sm-wrap { flex-wrap: wrap !important; } .flex-sm-nowrap { flex-wrap: nowrap !important; } .flex-sm-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-sm-0 { gap: 0 !important; } .gap-sm-1 { gap: 0.25rem !important; } .gap-sm-2 { gap: 0.5rem !important; } .gap-sm-3 { gap: 1rem !important; } .gap-sm-4 { gap: 1.5rem !important; } .gap-sm-5 { gap: 3rem !important; } .justify-content-sm-start { justify-content: flex-start !important; } .justify-content-sm-end { justify-content: flex-end !important; } .justify-content-sm-center { justify-content: center !important; } .justify-content-sm-between { justify-content: space-between !important; } .justify-content-sm-around { justify-content: space-around !important; } .justify-content-sm-evenly { justify-content: space-evenly !important; } .align-items-sm-start { align-items: flex-start !important; } .align-items-sm-end { align-items: flex-end !important; } .align-items-sm-center { align-items: center !important; } .align-items-sm-baseline { align-items: baseline !important; } .align-items-sm-stretch { align-items: stretch !important; } .align-content-sm-start { align-content: flex-start !important; } .align-content-sm-end { align-content: flex-end !important; } .align-content-sm-center { align-content: center !important; } .align-content-sm-between { align-content: space-between !important; } .align-content-sm-around { align-content: space-around !important; } .align-content-sm-stretch { align-content: stretch !important; } .align-self-sm-auto { align-self: auto !important; } .align-self-sm-start { align-self: flex-start !important; } .align-self-sm-end { align-self: flex-end !important; } .align-self-sm-center { align-self: center !important; } .align-self-sm-baseline { align-self: baseline !important; } .align-self-sm-stretch { align-self: stretch !important; } .order-sm-first { order: -1 !important; } .order-sm-0 { order: 0 !important; } .order-sm-1 { order: 1 !important; } .order-sm-2 { order: 2 !important; } .order-sm-3 { order: 3 !important; } .order-sm-4 { order: 4 !important; } .order-sm-5 { order: 5 !important; } .order-sm-last { order: 6 !important; } .m-sm-0 { margin: 0 !important; } .m-sm-1 { margin: 0.25rem !important; } .m-sm-2 { margin: 0.5rem !important; } .m-sm-3 { margin: 1rem !important; } .m-sm-4 { margin: 1.5rem !important; } .m-sm-5 { margin: 3rem !important; } .m-sm-auto { margin: auto !important; } .mx-sm-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-sm-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-sm-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-sm-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-sm-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-sm-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-sm-auto { margin-right: auto !important; margin-left: auto !important; } .my-sm-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-sm-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-sm-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-sm-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-sm-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-sm-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-sm-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-sm-0 { margin-top: 0 !important; } .mt-sm-1 { margin-top: 0.25rem !important; } .mt-sm-2 { margin-top: 0.5rem !important; } .mt-sm-3 { margin-top: 1rem !important; } .mt-sm-4 { margin-top: 1.5rem !important; } .mt-sm-5 { margin-top: 3rem !important; } .mt-sm-auto { margin-top: auto !important; } .me-sm-0 { margin-right: 0 !important; } .me-sm-1 { margin-right: 0.25rem !important; } .me-sm-2 { margin-right: 0.5rem !important; } .me-sm-3 { margin-right: 1rem !important; } .me-sm-4 { margin-right: 1.5rem !important; } .me-sm-5 { margin-right: 3rem !important; } .me-sm-auto { margin-right: auto !important; } .mb-sm-0 { margin-bottom: 0 !important; } .mb-sm-1 { margin-bottom: 0.25rem !important; } .mb-sm-2 { margin-bottom: 0.5rem !important; } .mb-sm-3 { margin-bottom: 1rem !important; } .mb-sm-4 { margin-bottom: 1.5rem !important; } .mb-sm-5 { margin-bottom: 3rem !important; } .mb-sm-auto { margin-bottom: auto !important; } .ms-sm-0 { margin-left: 0 !important; } .ms-sm-1 { margin-left: 0.25rem !important; } .ms-sm-2 { margin-left: 0.5rem !important; } .ms-sm-3 { margin-left: 1rem !important; } .ms-sm-4 { margin-left: 1.5rem !important; } .ms-sm-5 { margin-left: 3rem !important; } .ms-sm-auto { margin-left: auto !important; } .p-sm-0 { padding: 0 !important; } .p-sm-1 { padding: 0.25rem !important; } .p-sm-2 { padding: 0.5rem !important; } .p-sm-3 { padding: 1rem !important; } .p-sm-4 { padding: 1.5rem !important; } .p-sm-5 { padding: 3rem !important; } .px-sm-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-sm-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-sm-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-sm-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-sm-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-sm-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-sm-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-sm-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-sm-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-sm-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-sm-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-sm-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-sm-0 { padding-top: 0 !important; } .pt-sm-1 { padding-top: 0.25rem !important; } .pt-sm-2 { padding-top: 0.5rem !important; } .pt-sm-3 { padding-top: 1rem !important; } .pt-sm-4 { padding-top: 1.5rem !important; } .pt-sm-5 { padding-top: 3rem !important; } .pe-sm-0 { padding-right: 0 !important; } .pe-sm-1 { padding-right: 0.25rem !important; } .pe-sm-2 { padding-right: 0.5rem !important; } .pe-sm-3 { padding-right: 1rem !important; } .pe-sm-4 { padding-right: 1.5rem !important; } .pe-sm-5 { padding-right: 3rem !important; } .pb-sm-0 { padding-bottom: 0 !important; } .pb-sm-1 { padding-bottom: 0.25rem !important; } .pb-sm-2 { padding-bottom: 0.5rem !important; } .pb-sm-3 { padding-bottom: 1rem !important; } .pb-sm-4 { padding-bottom: 1.5rem !important; } .pb-sm-5 { padding-bottom: 3rem !important; } .ps-sm-0 { padding-left: 0 !important; } .ps-sm-1 { padding-left: 0.25rem !important; } .ps-sm-2 { padding-left: 0.5rem !important; } .ps-sm-3 { padding-left: 1rem !important; } .ps-sm-4 { padding-left: 1.5rem !important; } .ps-sm-5 { padding-left: 3rem !important; } .text-sm-start { text-align: left !important; } .text-sm-end { text-align: right !important; } .text-sm-center { text-align: center !important; } } @media (min-width: 768px) { .float-md-start { float: left !important; } .float-md-end { float: right !important; } .float-md-none { float: none !important; } .d-md-inline { display: inline !important; } .d-md-inline-block { display: inline-block !important; } .d-md-block { display: block !important; } .d-md-grid { display: grid !important; } .d-md-table { display: table !important; } .d-md-table-row { display: table-row !important; } .d-md-table-cell { display: table-cell !important; } .d-md-flex { display: flex !important; } .d-md-inline-flex { display: inline-flex !important; } .d-md-none { display: none !important; } .flex-md-fill { flex: 1 1 auto !important; } .flex-md-row { flex-direction: row !important; } .flex-md-column { flex-direction: column !important; } .flex-md-row-reverse { flex-direction: row-reverse !important; } .flex-md-column-reverse { flex-direction: column-reverse !important; } .flex-md-grow-0 { flex-grow: 0 !important; } .flex-md-grow-1 { flex-grow: 1 !important; } .flex-md-shrink-0 { flex-shrink: 0 !important; } .flex-md-shrink-1 { flex-shrink: 1 !important; } .flex-md-wrap { flex-wrap: wrap !important; } .flex-md-nowrap { flex-wrap: nowrap !important; } .flex-md-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-md-0 { gap: 0 !important; } .gap-md-1 { gap: 0.25rem !important; } .gap-md-2 { gap: 0.5rem !important; } .gap-md-3 { gap: 1rem !important; } .gap-md-4 { gap: 1.5rem !important; } .gap-md-5 { gap: 3rem !important; } .justify-content-md-start { justify-content: flex-start !important; } .justify-content-md-end { justify-content: flex-end !important; } .justify-content-md-center { justify-content: center !important; } .justify-content-md-between { justify-content: space-between !important; } .justify-content-md-around { justify-content: space-around !important; } .justify-content-md-evenly { justify-content: space-evenly !important; } .align-items-md-start { align-items: flex-start !important; } .align-items-md-end { align-items: flex-end !important; } .align-items-md-center { align-items: center !important; } .align-items-md-baseline { align-items: baseline !important; } .align-items-md-stretch { align-items: stretch !important; } .align-content-md-start { align-content: flex-start !important; } .align-content-md-end { align-content: flex-end !important; } .align-content-md-center { align-content: center !important; } .align-content-md-between { align-content: space-between !important; } .align-content-md-around { align-content: space-around !important; } .align-content-md-stretch { align-content: stretch !important; } .align-self-md-auto { align-self: auto !important; } .align-self-md-start { align-self: flex-start !important; } .align-self-md-end { align-self: flex-end !important; } .align-self-md-center { align-self: center !important; } .align-self-md-baseline { align-self: baseline !important; } .align-self-md-stretch { align-self: stretch !important; } .order-md-first { order: -1 !important; } .order-md-0 { order: 0 !important; } .order-md-1 { order: 1 !important; } .order-md-2 { order: 2 !important; } .order-md-3 { order: 3 !important; } .order-md-4 { order: 4 !important; } .order-md-5 { order: 5 !important; } .order-md-last { order: 6 !important; } .m-md-0 { margin: 0 !important; } .m-md-1 { margin: 0.25rem !important; } .m-md-2 { margin: 0.5rem !important; } .m-md-3 { margin: 1rem !important; } .m-md-4 { margin: 1.5rem !important; } .m-md-5 { margin: 3rem !important; } .m-md-auto { margin: auto !important; } .mx-md-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-md-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-md-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-md-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-md-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-md-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-md-auto { margin-right: auto !important; margin-left: auto !important; } .my-md-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-md-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-md-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-md-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-md-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-md-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-md-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-md-0 { margin-top: 0 !important; } .mt-md-1 { margin-top: 0.25rem !important; } .mt-md-2 { margin-top: 0.5rem !important; } .mt-md-3 { margin-top: 1rem !important; } .mt-md-4 { margin-top: 1.5rem !important; } .mt-md-5 { margin-top: 3rem !important; } .mt-md-auto { margin-top: auto !important; } .me-md-0 { margin-right: 0 !important; } .me-md-1 { margin-right: 0.25rem !important; } .me-md-2 { margin-right: 0.5rem !important; } .me-md-3 { margin-right: 1rem !important; } .me-md-4 { margin-right: 1.5rem !important; } .me-md-5 { margin-right: 3rem !important; } .me-md-auto { margin-right: auto !important; } .mb-md-0 { margin-bottom: 0 !important; } .mb-md-1 { margin-bottom: 0.25rem !important; } .mb-md-2 { margin-bottom: 0.5rem !important; } .mb-md-3 { margin-bottom: 1rem !important; } .mb-md-4 { margin-bottom: 1.5rem !important; } .mb-md-5 { margin-bottom: 3rem !important; } .mb-md-auto { margin-bottom: auto !important; } .ms-md-0 { margin-left: 0 !important; } .ms-md-1 { margin-left: 0.25rem !important; } .ms-md-2 { margin-left: 0.5rem !important; } .ms-md-3 { margin-left: 1rem !important; } .ms-md-4 { margin-left: 1.5rem !important; } .ms-md-5 { margin-left: 3rem !important; } .ms-md-auto { margin-left: auto !important; } .p-md-0 { padding: 0 !important; } .p-md-1 { padding: 0.25rem !important; } .p-md-2 { padding: 0.5rem !important; } .p-md-3 { padding: 1rem !important; } .p-md-4 { padding: 1.5rem !important; } .p-md-5 { padding: 3rem !important; } .px-md-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-md-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-md-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-md-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-md-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-md-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-md-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-md-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-md-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-md-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-md-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-md-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-md-0 { padding-top: 0 !important; } .pt-md-1 { padding-top: 0.25rem !important; } .pt-md-2 { padding-top: 0.5rem !important; } .pt-md-3 { padding-top: 1rem !important; } .pt-md-4 { padding-top: 1.5rem !important; } .pt-md-5 { padding-top: 3rem !important; } .pe-md-0 { padding-right: 0 !important; } .pe-md-1 { padding-right: 0.25rem !important; } .pe-md-2 { padding-right: 0.5rem !important; } .pe-md-3 { padding-right: 1rem !important; } .pe-md-4 { padding-right: 1.5rem !important; } .pe-md-5 { padding-right: 3rem !important; } .pb-md-0 { padding-bottom: 0 !important; } .pb-md-1 { padding-bottom: 0.25rem !important; } .pb-md-2 { padding-bottom: 0.5rem !important; } .pb-md-3 { padding-bottom: 1rem !important; } .pb-md-4 { padding-bottom: 1.5rem !important; } .pb-md-5 { padding-bottom: 3rem !important; } .ps-md-0 { padding-left: 0 !important; } .ps-md-1 { padding-left: 0.25rem !important; } .ps-md-2 { padding-left: 0.5rem !important; } .ps-md-3 { padding-left: 1rem !important; } .ps-md-4 { padding-left: 1.5rem !important; } .ps-md-5 { padding-left: 3rem !important; } .text-md-start { text-align: left !important; } .text-md-end { text-align: right !important; } .text-md-center { text-align: center !important; } } @media (min-width: 992px) { .float-lg-start { float: left !important; } .float-lg-end { float: right !important; } .float-lg-none { float: none !important; } .d-lg-inline { display: inline !important; } .d-lg-inline-block { display: inline-block !important; } .d-lg-block { display: block !important; } .d-lg-grid { display: grid !important; } .d-lg-table { display: table !important; } .d-lg-table-row { display: table-row !important; } .d-lg-table-cell { display: table-cell !important; } .d-lg-flex { display: flex !important; } .d-lg-inline-flex { display: inline-flex !important; } .d-lg-none { display: none !important; } .flex-lg-fill { flex: 1 1 auto !important; } .flex-lg-row { flex-direction: row !important; } .flex-lg-column { flex-direction: column !important; } .flex-lg-row-reverse { flex-direction: row-reverse !important; } .flex-lg-column-reverse { flex-direction: column-reverse !important; } .flex-lg-grow-0 { flex-grow: 0 !important; } .flex-lg-grow-1 { flex-grow: 1 !important; } .flex-lg-shrink-0 { flex-shrink: 0 !important; } .flex-lg-shrink-1 { flex-shrink: 1 !important; } .flex-lg-wrap { flex-wrap: wrap !important; } .flex-lg-nowrap { flex-wrap: nowrap !important; } .flex-lg-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-lg-0 { gap: 0 !important; } .gap-lg-1 { gap: 0.25rem !important; } .gap-lg-2 { gap: 0.5rem !important; } .gap-lg-3 { gap: 1rem !important; } .gap-lg-4 { gap: 1.5rem !important; } .gap-lg-5 { gap: 3rem !important; } .justify-content-lg-start { justify-content: flex-start !important; } .justify-content-lg-end { justify-content: flex-end !important; } .justify-content-lg-center { justify-content: center !important; } .justify-content-lg-between { justify-content: space-between !important; } .justify-content-lg-around { justify-content: space-around !important; } .justify-content-lg-evenly { justify-content: space-evenly !important; } .align-items-lg-start { align-items: flex-start !important; } .align-items-lg-end { align-items: flex-end !important; } .align-items-lg-center { align-items: center !important; } .align-items-lg-baseline { align-items: baseline !important; } .align-items-lg-stretch { align-items: stretch !important; } .align-content-lg-start { align-content: flex-start !important; } .align-content-lg-end { align-content: flex-end !important; } .align-content-lg-center { align-content: center !important; } .align-content-lg-between { align-content: space-between !important; } .align-content-lg-around { align-content: space-around !important; } .align-content-lg-stretch { align-content: stretch !important; } .align-self-lg-auto { align-self: auto !important; } .align-self-lg-start { align-self: flex-start !important; } .align-self-lg-end { align-self: flex-end !important; } .align-self-lg-center { align-self: center !important; } .align-self-lg-baseline { align-self: baseline !important; } .align-self-lg-stretch { align-self: stretch !important; } .order-lg-first { order: -1 !important; } .order-lg-0 { order: 0 !important; } .order-lg-1 { order: 1 !important; } .order-lg-2 { order: 2 !important; } .order-lg-3 { order: 3 !important; } .order-lg-4 { order: 4 !important; } .order-lg-5 { order: 5 !important; } .order-lg-last { order: 6 !important; } .m-lg-0 { margin: 0 !important; } .m-lg-1 { margin: 0.25rem !important; } .m-lg-2 { margin: 0.5rem !important; } .m-lg-3 { margin: 1rem !important; } .m-lg-4 { margin: 1.5rem !important; } .m-lg-5 { margin: 3rem !important; } .m-lg-auto { margin: auto !important; } .mx-lg-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-lg-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-lg-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-lg-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-lg-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-lg-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-lg-auto { margin-right: auto !important; margin-left: auto !important; } .my-lg-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-lg-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-lg-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-lg-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-lg-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-lg-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-lg-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-lg-0 { margin-top: 0 !important; } .mt-lg-1 { margin-top: 0.25rem !important; } .mt-lg-2 { margin-top: 0.5rem !important; } .mt-lg-3 { margin-top: 1rem !important; } .mt-lg-4 { margin-top: 1.5rem !important; } .mt-lg-5 { margin-top: 3rem !important; } .mt-lg-auto { margin-top: auto !important; } .me-lg-0 { margin-right: 0 !important; } .me-lg-1 { margin-right: 0.25rem !important; } .me-lg-2 { margin-right: 0.5rem !important; } .me-lg-3 { margin-right: 1rem !important; } .me-lg-4 { margin-right: 1.5rem !important; } .me-lg-5 { margin-right: 3rem !important; } .me-lg-auto { margin-right: auto !important; } .mb-lg-0 { margin-bottom: 0 !important; } .mb-lg-1 { margin-bottom: 0.25rem !important; } .mb-lg-2 { margin-bottom: 0.5rem !important; } .mb-lg-3 { margin-bottom: 1rem !important; } .mb-lg-4 { margin-bottom: 1.5rem !important; } .mb-lg-5 { margin-bottom: 3rem !important; } .mb-lg-auto { margin-bottom: auto !important; } .ms-lg-0 { margin-left: 0 !important; } .ms-lg-1 { margin-left: 0.25rem !important; } .ms-lg-2 { margin-left: 0.5rem !important; } .ms-lg-3 { margin-left: 1rem !important; } .ms-lg-4 { margin-left: 1.5rem !important; } .ms-lg-5 { margin-left: 3rem !important; } .ms-lg-auto { margin-left: auto !important; } .p-lg-0 { padding: 0 !important; } .p-lg-1 { padding: 0.25rem !important; } .p-lg-2 { padding: 0.5rem !important; } .p-lg-3 { padding: 1rem !important; } .p-lg-4 { padding: 1.5rem !important; } .p-lg-5 { padding: 3rem !important; } .px-lg-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-lg-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-lg-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-lg-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-lg-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-lg-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-lg-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-lg-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-lg-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-lg-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-lg-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-lg-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-lg-0 { padding-top: 0 !important; } .pt-lg-1 { padding-top: 0.25rem !important; } .pt-lg-2 { padding-top: 0.5rem !important; } .pt-lg-3 { padding-top: 1rem !important; } .pt-lg-4 { padding-top: 1.5rem !important; } .pt-lg-5 { padding-top: 3rem !important; } .pe-lg-0 { padding-right: 0 !important; } .pe-lg-1 { padding-right: 0.25rem !important; } .pe-lg-2 { padding-right: 0.5rem !important; } .pe-lg-3 { padding-right: 1rem !important; } .pe-lg-4 { padding-right: 1.5rem !important; } .pe-lg-5 { padding-right: 3rem !important; } .pb-lg-0 { padding-bottom: 0 !important; } .pb-lg-1 { padding-bottom: 0.25rem !important; } .pb-lg-2 { padding-bottom: 0.5rem !important; } .pb-lg-3 { padding-bottom: 1rem !important; } .pb-lg-4 { padding-bottom: 1.5rem !important; } .pb-lg-5 { padding-bottom: 3rem !important; } .ps-lg-0 { padding-left: 0 !important; } .ps-lg-1 { padding-left: 0.25rem !important; } .ps-lg-2 { padding-left: 0.5rem !important; } .ps-lg-3 { padding-left: 1rem !important; } .ps-lg-4 { padding-left: 1.5rem !important; } .ps-lg-5 { padding-left: 3rem !important; } .text-lg-start { text-align: left !important; } .text-lg-end { text-align: right !important; } .text-lg-center { text-align: center !important; } } @media (min-width: 1200px) { .float-xl-start { float: left !important; } .float-xl-end { float: right !important; } .float-xl-none { float: none !important; } .d-xl-inline { display: inline !important; } .d-xl-inline-block { display: inline-block !important; } .d-xl-block { display: block !important; } .d-xl-grid { display: grid !important; } .d-xl-table { display: table !important; } .d-xl-table-row { display: table-row !important; } .d-xl-table-cell { display: table-cell !important; } .d-xl-flex { display: flex !important; } .d-xl-inline-flex { display: inline-flex !important; } .d-xl-none { display: none !important; } .flex-xl-fill { flex: 1 1 auto !important; } .flex-xl-row { flex-direction: row !important; } .flex-xl-column { flex-direction: column !important; } .flex-xl-row-reverse { flex-direction: row-reverse !important; } .flex-xl-column-reverse { flex-direction: column-reverse !important; } .flex-xl-grow-0 { flex-grow: 0 !important; } .flex-xl-grow-1 { flex-grow: 1 !important; } .flex-xl-shrink-0 { flex-shrink: 0 !important; } .flex-xl-shrink-1 { flex-shrink: 1 !important; } .flex-xl-wrap { flex-wrap: wrap !important; } .flex-xl-nowrap { flex-wrap: nowrap !important; } .flex-xl-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-xl-0 { gap: 0 !important; } .gap-xl-1 { gap: 0.25rem !important; } .gap-xl-2 { gap: 0.5rem !important; } .gap-xl-3 { gap: 1rem !important; } .gap-xl-4 { gap: 1.5rem !important; } .gap-xl-5 { gap: 3rem !important; } .justify-content-xl-start { justify-content: flex-start !important; } .justify-content-xl-end { justify-content: flex-end !important; } .justify-content-xl-center { justify-content: center !important; } .justify-content-xl-between { justify-content: space-between !important; } .justify-content-xl-around { justify-content: space-around !important; } .justify-content-xl-evenly { justify-content: space-evenly !important; } .align-items-xl-start { align-items: flex-start !important; } .align-items-xl-end { align-items: flex-end !important; } .align-items-xl-center { align-items: center !important; } .align-items-xl-baseline { align-items: baseline !important; } .align-items-xl-stretch { align-items: stretch !important; } .align-content-xl-start { align-content: flex-start !important; } .align-content-xl-end { align-content: flex-end !important; } .align-content-xl-center { align-content: center !important; } .align-content-xl-between { align-content: space-between !important; } .align-content-xl-around { align-content: space-around !important; } .align-content-xl-stretch { align-content: stretch !important; } .align-self-xl-auto { align-self: auto !important; } .align-self-xl-start { align-self: flex-start !important; } .align-self-xl-end { align-self: flex-end !important; } .align-self-xl-center { align-self: center !important; } .align-self-xl-baseline { align-self: baseline !important; } .align-self-xl-stretch { align-self: stretch !important; } .order-xl-first { order: -1 !important; } .order-xl-0 { order: 0 !important; } .order-xl-1 { order: 1 !important; } .order-xl-2 { order: 2 !important; } .order-xl-3 { order: 3 !important; } .order-xl-4 { order: 4 !important; } .order-xl-5 { order: 5 !important; } .order-xl-last { order: 6 !important; } .m-xl-0 { margin: 0 !important; } .m-xl-1 { margin: 0.25rem !important; } .m-xl-2 { margin: 0.5rem !important; } .m-xl-3 { margin: 1rem !important; } .m-xl-4 { margin: 1.5rem !important; } .m-xl-5 { margin: 3rem !important; } .m-xl-auto { margin: auto !important; } .mx-xl-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-xl-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-xl-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-xl-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-xl-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-xl-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-xl-auto { margin-right: auto !important; margin-left: auto !important; } .my-xl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xl-0 { margin-top: 0 !important; } .mt-xl-1 { margin-top: 0.25rem !important; } .mt-xl-2 { margin-top: 0.5rem !important; } .mt-xl-3 { margin-top: 1rem !important; } .mt-xl-4 { margin-top: 1.5rem !important; } .mt-xl-5 { margin-top: 3rem !important; } .mt-xl-auto { margin-top: auto !important; } .me-xl-0 { margin-right: 0 !important; } .me-xl-1 { margin-right: 0.25rem !important; } .me-xl-2 { margin-right: 0.5rem !important; } .me-xl-3 { margin-right: 1rem !important; } .me-xl-4 { margin-right: 1.5rem !important; } .me-xl-5 { margin-right: 3rem !important; } .me-xl-auto { margin-right: auto !important; } .mb-xl-0 { margin-bottom: 0 !important; } .mb-xl-1 { margin-bottom: 0.25rem !important; } .mb-xl-2 { margin-bottom: 0.5rem !important; } .mb-xl-3 { margin-bottom: 1rem !important; } .mb-xl-4 { margin-bottom: 1.5rem !important; } .mb-xl-5 { margin-bottom: 3rem !important; } .mb-xl-auto { margin-bottom: auto !important; } .ms-xl-0 { margin-left: 0 !important; } .ms-xl-1 { margin-left: 0.25rem !important; } .ms-xl-2 { margin-left: 0.5rem !important; } .ms-xl-3 { margin-left: 1rem !important; } .ms-xl-4 { margin-left: 1.5rem !important; } .ms-xl-5 { margin-left: 3rem !important; } .ms-xl-auto { margin-left: auto !important; } .p-xl-0 { padding: 0 !important; } .p-xl-1 { padding: 0.25rem !important; } .p-xl-2 { padding: 0.5rem !important; } .p-xl-3 { padding: 1rem !important; } .p-xl-4 { padding: 1.5rem !important; } .p-xl-5 { padding: 3rem !important; } .px-xl-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-xl-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-xl-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-xl-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-xl-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-xl-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-xl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xl-0 { padding-top: 0 !important; } .pt-xl-1 { padding-top: 0.25rem !important; } .pt-xl-2 { padding-top: 0.5rem !important; } .pt-xl-3 { padding-top: 1rem !important; } .pt-xl-4 { padding-top: 1.5rem !important; } .pt-xl-5 { padding-top: 3rem !important; } .pe-xl-0 { padding-right: 0 !important; } .pe-xl-1 { padding-right: 0.25rem !important; } .pe-xl-2 { padding-right: 0.5rem !important; } .pe-xl-3 { padding-right: 1rem !important; } .pe-xl-4 { padding-right: 1.5rem !important; } .pe-xl-5 { padding-right: 3rem !important; } .pb-xl-0 { padding-bottom: 0 !important; } .pb-xl-1 { padding-bottom: 0.25rem !important; } .pb-xl-2 { padding-bottom: 0.5rem !important; } .pb-xl-3 { padding-bottom: 1rem !important; } .pb-xl-4 { padding-bottom: 1.5rem !important; } .pb-xl-5 { padding-bottom: 3rem !important; } .ps-xl-0 { padding-left: 0 !important; } .ps-xl-1 { padding-left: 0.25rem !important; } .ps-xl-2 { padding-left: 0.5rem !important; } .ps-xl-3 { padding-left: 1rem !important; } .ps-xl-4 { padding-left: 1.5rem !important; } .ps-xl-5 { padding-left: 3rem !important; } .text-xl-start { text-align: left !important; } .text-xl-end { text-align: right !important; } .text-xl-center { text-align: center !important; } } @media (min-width: 1400px) { .float-xxl-start { float: left !important; } .float-xxl-end { float: right !important; } .float-xxl-none { float: none !important; } .d-xxl-inline { display: inline !important; } .d-xxl-inline-block { display: inline-block !important; } .d-xxl-block { display: block !important; } .d-xxl-grid { display: grid !important; } .d-xxl-table { display: table !important; } .d-xxl-table-row { display: table-row !important; } .d-xxl-table-cell { display: table-cell !important; } .d-xxl-flex { display: flex !important; } .d-xxl-inline-flex { display: inline-flex !important; } .d-xxl-none { display: none !important; } .flex-xxl-fill { flex: 1 1 auto !important; } .flex-xxl-row { flex-direction: row !important; } .flex-xxl-column { flex-direction: column !important; } .flex-xxl-row-reverse { flex-direction: row-reverse !important; } .flex-xxl-column-reverse { flex-direction: column-reverse !important; } .flex-xxl-grow-0 { flex-grow: 0 !important; } .flex-xxl-grow-1 { flex-grow: 1 !important; } .flex-xxl-shrink-0 { flex-shrink: 0 !important; } .flex-xxl-shrink-1 { flex-shrink: 1 !important; } .flex-xxl-wrap { flex-wrap: wrap !important; } .flex-xxl-nowrap { flex-wrap: nowrap !important; } .flex-xxl-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-xxl-0 { gap: 0 !important; } .gap-xxl-1 { gap: 0.25rem !important; } .gap-xxl-2 { gap: 0.5rem !important; } .gap-xxl-3 { gap: 1rem !important; } .gap-xxl-4 { gap: 1.5rem !important; } .gap-xxl-5 { gap: 3rem !important; } .justify-content-xxl-start { justify-content: flex-start !important; } .justify-content-xxl-end { justify-content: flex-end !important; } .justify-content-xxl-center { justify-content: center !important; } .justify-content-xxl-between { justify-content: space-between !important; } .justify-content-xxl-around { justify-content: space-around !important; } .justify-content-xxl-evenly { justify-content: space-evenly !important; } .align-items-xxl-start { align-items: flex-start !important; } .align-items-xxl-end { align-items: flex-end !important; } .align-items-xxl-center { align-items: center !important; } .align-items-xxl-baseline { align-items: baseline !important; } .align-items-xxl-stretch { align-items: stretch !important; } .align-content-xxl-start { align-content: flex-start !important; } .align-content-xxl-end { align-content: flex-end !important; } .align-content-xxl-center { align-content: center !important; } .align-content-xxl-between { align-content: space-between !important; } .align-content-xxl-around { align-content: space-around !important; } .align-content-xxl-stretch { align-content: stretch !important; } .align-self-xxl-auto { align-self: auto !important; } .align-self-xxl-start { align-self: flex-start !important; } .align-self-xxl-end { align-self: flex-end !important; } .align-self-xxl-center { align-self: center !important; } .align-self-xxl-baseline { align-self: baseline !important; } .align-self-xxl-stretch { align-self: stretch !important; } .order-xxl-first { order: -1 !important; } .order-xxl-0 { order: 0 !important; } .order-xxl-1 { order: 1 !important; } .order-xxl-2 { order: 2 !important; } .order-xxl-3 { order: 3 !important; } .order-xxl-4 { order: 4 !important; } .order-xxl-5 { order: 5 !important; } .order-xxl-last { order: 6 !important; } .m-xxl-0 { margin: 0 !important; } .m-xxl-1 { margin: 0.25rem !important; } .m-xxl-2 { margin: 0.5rem !important; } .m-xxl-3 { margin: 1rem !important; } .m-xxl-4 { margin: 1.5rem !important; } .m-xxl-5 { margin: 3rem !important; } .m-xxl-auto { margin: auto !important; } .mx-xxl-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-xxl-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-xxl-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-xxl-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-xxl-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-xxl-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-xxl-auto { margin-right: auto !important; margin-left: auto !important; } .my-xxl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xxl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xxl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xxl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xxl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xxl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xxl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xxl-0 { margin-top: 0 !important; } .mt-xxl-1 { margin-top: 0.25rem !important; } .mt-xxl-2 { margin-top: 0.5rem !important; } .mt-xxl-3 { margin-top: 1rem !important; } .mt-xxl-4 { margin-top: 1.5rem !important; } .mt-xxl-5 { margin-top: 3rem !important; } .mt-xxl-auto { margin-top: auto !important; } .me-xxl-0 { margin-right: 0 !important; } .me-xxl-1 { margin-right: 0.25rem !important; } .me-xxl-2 { margin-right: 0.5rem !important; } .me-xxl-3 { margin-right: 1rem !important; } .me-xxl-4 { margin-right: 1.5rem !important; } .me-xxl-5 { margin-right: 3rem !important; } .me-xxl-auto { margin-right: auto !important; } .mb-xxl-0 { margin-bottom: 0 !important; } .mb-xxl-1 { margin-bottom: 0.25rem !important; } .mb-xxl-2 { margin-bottom: 0.5rem !important; } .mb-xxl-3 { margin-bottom: 1rem !important; } .mb-xxl-4 { margin-bottom: 1.5rem !important; } .mb-xxl-5 { margin-bottom: 3rem !important; } .mb-xxl-auto { margin-bottom: auto !important; } .ms-xxl-0 { margin-left: 0 !important; } .ms-xxl-1 { margin-left: 0.25rem !important; } .ms-xxl-2 { margin-left: 0.5rem !important; } .ms-xxl-3 { margin-left: 1rem !important; } .ms-xxl-4 { margin-left: 1.5rem !important; } .ms-xxl-5 { margin-left: 3rem !important; } .ms-xxl-auto { margin-left: auto !important; } .p-xxl-0 { padding: 0 !important; } .p-xxl-1 { padding: 0.25rem !important; } .p-xxl-2 { padding: 0.5rem !important; } .p-xxl-3 { padding: 1rem !important; } .p-xxl-4 { padding: 1.5rem !important; } .p-xxl-5 { padding: 3rem !important; } .px-xxl-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-xxl-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-xxl-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-xxl-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-xxl-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-xxl-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-xxl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xxl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xxl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xxl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xxl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xxl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xxl-0 { padding-top: 0 !important; } .pt-xxl-1 { padding-top: 0.25rem !important; } .pt-xxl-2 { padding-top: 0.5rem !important; } .pt-xxl-3 { padding-top: 1rem !important; } .pt-xxl-4 { padding-top: 1.5rem !important; } .pt-xxl-5 { padding-top: 3rem !important; } .pe-xxl-0 { padding-right: 0 !important; } .pe-xxl-1 { padding-right: 0.25rem !important; } .pe-xxl-2 { padding-right: 0.5rem !important; } .pe-xxl-3 { padding-right: 1rem !important; } .pe-xxl-4 { padding-right: 1.5rem !important; } .pe-xxl-5 { padding-right: 3rem !important; } .pb-xxl-0 { padding-bottom: 0 !important; } .pb-xxl-1 { padding-bottom: 0.25rem !important; } .pb-xxl-2 { padding-bottom: 0.5rem !important; } .pb-xxl-3 { padding-bottom: 1rem !important; } .pb-xxl-4 { padding-bottom: 1.5rem !important; } .pb-xxl-5 { padding-bottom: 3rem !important; } .ps-xxl-0 { padding-left: 0 !important; } .ps-xxl-1 { padding-left: 0.25rem !important; } .ps-xxl-2 { padding-left: 0.5rem !important; } .ps-xxl-3 { padding-left: 1rem !important; } .ps-xxl-4 { padding-left: 1.5rem !important; } .ps-xxl-5 { padding-left: 3rem !important; } .text-xxl-start { text-align: left !important; } .text-xxl-end { text-align: right !important; } .text-xxl-center { text-align: center !important; } } @media (min-width: 1200px) { .fs-1 { font-size: 2.5rem !important; } .fs-2 { font-size: 2rem !important; } .fs-3 { font-size: 1.75rem !important; } .fs-4 { font-size: 1.5rem !important; } } @media print { .d-print-inline { display: inline !important; } .d-print-inline-block { display: inline-block !important; } .d-print-block { display: block !important; } .d-print-grid { display: grid !important; } .d-print-table { display: table !important; } .d-print-table-row { display: table-row !important; } .d-print-table-cell { display: table-cell !important; } .d-print-flex { display: flex !important; } .d-print-inline-flex { display: inline-flex !important; } .d-print-none { display: none !important; } } /*# sourceMappingURL=bootstrap.css.map */ ================================================ FILE: examples/tailwind-and-bootstrap-example/css-modules/bootstrap/bootstrap.rtl.css ================================================ @charset "UTF-8"; /*! * Bootstrap v5.1.3 (https://getbootstrap.com/) * Copyright 2011-2021 The Bootstrap Authors * Copyright 2011-2021 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ :root { --bs-blue: #0d6efd; --bs-indigo: #6610f2; --bs-purple: #6f42c1; --bs-pink: #d63384; --bs-red: #dc3545; --bs-orange: #fd7e14; --bs-yellow: #ffc107; --bs-green: #198754; --bs-teal: #20c997; --bs-cyan: #0dcaf0; --bs-white: #fff; --bs-gray: #6c757d; --bs-gray-dark: #343a40; --bs-gray-100: #f8f9fa; --bs-gray-200: #e9ecef; --bs-gray-300: #dee2e6; --bs-gray-400: #ced4da; --bs-gray-500: #adb5bd; --bs-gray-600: #6c757d; --bs-gray-700: #495057; --bs-gray-800: #343a40; --bs-gray-900: #212529; --bs-primary: #0d6efd; --bs-secondary: #6c757d; --bs-success: #198754; --bs-info: #0dcaf0; --bs-warning: #ffc107; --bs-danger: #dc3545; --bs-light: #f8f9fa; --bs-dark: #212529; --bs-primary-rgb: 13, 110, 253; --bs-secondary-rgb: 108, 117, 125; --bs-success-rgb: 25, 135, 84; --bs-info-rgb: 13, 202, 240; --bs-warning-rgb: 255, 193, 7; --bs-danger-rgb: 220, 53, 69; --bs-light-rgb: 248, 249, 250; --bs-dark-rgb: 33, 37, 41; --bs-white-rgb: 255, 255, 255; --bs-black-rgb: 0, 0, 0; --bs-body-color-rgb: 33, 37, 41; --bs-body-bg-rgb: 255, 255, 255; --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --bs-gradient: linear-gradient( 180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0) ); --bs-body-font-family: var(--bs-font-sans-serif); --bs-body-font-size: 1rem; --bs-body-font-weight: 400; --bs-body-line-height: 1.5; --bs-body-color: #212529; --bs-body-bg: #fff; } *, *::before, *::after { box-sizing: border-box; } @media (prefers-reduced-motion: no-preference) { :root { scroll-behavior: smooth; } } body { margin: 0; font-family: var(--bs-body-font-family); font-size: var(--bs-body-font-size); font-weight: var(--bs-body-font-weight); line-height: var(--bs-body-line-height); color: var(--bs-body-color); text-align: var(--bs-body-text-align); background-color: var(--bs-body-bg); -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } hr { margin: 1rem 0; color: inherit; background-color: currentColor; border: 0; opacity: 0.25; } hr:not([size]) { height: 1px; } h6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 { margin-top: 0; margin-bottom: 0.5rem; font-weight: 500; line-height: 1.2; } h1, .h1 { font-size: calc(1.375rem + 1.5vw); } @media (min-width: 1200px) { h1, .h1 { font-size: 2.5rem; } } h2, .h2 { font-size: calc(1.325rem + 0.9vw); } @media (min-width: 1200px) { h2, .h2 { font-size: 2rem; } } h3, .h3 { font-size: calc(1.3rem + 0.6vw); } @media (min-width: 1200px) { h3, .h3 { font-size: 1.75rem; } } h4, .h4 { font-size: calc(1.275rem + 0.3vw); } @media (min-width: 1200px) { h4, .h4 { font-size: 1.5rem; } } h5, .h5 { font-size: 1.25rem; } h6, .h6 { font-size: 1rem; } p { margin-top: 0; margin-bottom: 1rem; } abbr[title], abbr[data-bs-original-title] { -webkit-text-decoration: underline dotted; text-decoration: underline dotted; cursor: help; -webkit-text-decoration-skip-ink: none; text-decoration-skip-ink: none; } address { margin-bottom: 1rem; font-style: normal; line-height: inherit; } ol, ul { padding-right: 2rem; } ol, ul, dl { margin-top: 0; margin-bottom: 1rem; } ol ol, ul ul, ol ul, ul ol { margin-bottom: 0; } dt { font-weight: 700; } dd { margin-bottom: 0.5rem; margin-right: 0; } blockquote { margin: 0 0 1rem; } b, strong { font-weight: bolder; } small, .small { font-size: 0.875em; } mark, .mark { padding: 0.2em; background-color: #fcf8e3; } sub, sup { position: relative; font-size: 0.75em; line-height: 0; vertical-align: baseline; } sub { bottom: -0.25em; } sup { top: -0.5em; } a { color: #0d6efd; text-decoration: underline; } a:hover { color: #0a58ca; } a:not([href]):not([class]), a:not([href]):not([class]):hover { color: inherit; text-decoration: none; } pre, code, kbd, samp { font-family: var(--bs-font-monospace); font-size: 1em; direction: ltr; unicode-bidi: bidi-override; } pre { display: block; margin-top: 0; margin-bottom: 1rem; overflow: auto; font-size: 0.875em; } pre code { font-size: inherit; color: inherit; word-break: normal; } code { font-size: 0.875em; color: #d63384; word-wrap: break-word; } a > code { color: inherit; } kbd { padding: 0.2rem 0.4rem; font-size: 0.875em; color: #fff; background-color: #212529; border-radius: 0.2rem; } kbd kbd { padding: 0; font-size: 1em; font-weight: 700; } figure { margin: 0 0 1rem; } img, svg { vertical-align: middle; } table { caption-side: bottom; border-collapse: collapse; } caption { padding-top: 0.5rem; padding-bottom: 0.5rem; color: #6c757d; text-align: right; } th { text-align: inherit; text-align: -webkit-match-parent; } thead, tbody, tfoot, tr, td, th { border-color: inherit; border-style: solid; border-width: 0; } label { display: inline-block; } button { border-radius: 0; } button:focus:not(:focus-visible) { outline: 0; } input, button, select, optgroup, textarea { margin: 0; font-family: inherit; font-size: inherit; line-height: inherit; } button, select { text-transform: none; } [role="button"] { cursor: pointer; } select { word-wrap: normal; } select:disabled { opacity: 1; } [list]::-webkit-calendar-picker-indicator { display: none; } button, [type="button"], [type="reset"], [type="submit"] { -webkit-appearance: button; } button:not(:disabled), [type="button"]:not(:disabled), [type="reset"]:not(:disabled), [type="submit"]:not(:disabled) { cursor: pointer; } ::-moz-focus-inner { padding: 0; border-style: none; } textarea { resize: vertical; } fieldset { min-width: 0; padding: 0; margin: 0; border: 0; } legend { float: right; width: 100%; padding: 0; margin-bottom: 0.5rem; font-size: calc(1.275rem + 0.3vw); line-height: inherit; } @media (min-width: 1200px) { legend { font-size: 1.5rem; } } legend + * { clear: right; } ::-webkit-datetime-edit-fields-wrapper, ::-webkit-datetime-edit-text, ::-webkit-datetime-edit-minute, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-year-field { padding: 0; } ::-webkit-inner-spin-button { height: auto; } [type="search"] { outline-offset: -2px; -webkit-appearance: textfield; } [type="tel"], [type="url"], [type="email"], [type="number"] { direction: ltr; } ::-webkit-search-decoration { -webkit-appearance: none; } ::-webkit-color-swatch-wrapper { padding: 0; } ::-webkit-file-upload-button { font: inherit; } ::file-selector-button { font: inherit; } ::-webkit-file-upload-button { font: inherit; -webkit-appearance: button; } output { display: inline-block; } iframe { border: 0; } summary { display: list-item; cursor: pointer; } progress { vertical-align: baseline; } [hidden] { display: none !important; } .lead { font-size: 1.25rem; font-weight: 300; } .display-1 { font-size: calc(1.625rem + 4.5vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-1 { font-size: 5rem; } } .display-2 { font-size: calc(1.575rem + 3.9vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-2 { font-size: 4.5rem; } } .display-3 { font-size: calc(1.525rem + 3.3vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-3 { font-size: 4rem; } } .display-4 { font-size: calc(1.475rem + 2.7vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-4 { font-size: 3.5rem; } } .display-5 { font-size: calc(1.425rem + 2.1vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-5 { font-size: 3rem; } } .display-6 { font-size: calc(1.375rem + 1.5vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-6 { font-size: 2.5rem; } } .list-unstyled { padding-right: 0; list-style: none; } .list-inline { padding-right: 0; list-style: none; } .list-inline-item { display: inline-block; } .list-inline-item:not(:last-child) { margin-left: 0.5rem; } .initialism { font-size: 0.875em; text-transform: uppercase; } .blockquote { margin-bottom: 1rem; font-size: 1.25rem; } .blockquote > :last-child { margin-bottom: 0; } .blockquote-footer { margin-top: -1rem; margin-bottom: 1rem; font-size: 0.875em; color: #6c757d; } .blockquote-footer::before { content: "— "; } .img-fluid { max-width: 100%; height: auto; } .img-thumbnail { padding: 0.25rem; background-color: #fff; border: 1px solid #dee2e6; border-radius: 0.25rem; max-width: 100%; height: auto; } .figure { display: inline-block; } .figure-img { margin-bottom: 0.5rem; line-height: 1; } .figure-caption { font-size: 0.875em; color: #6c757d; } .container, .container-fluid, .container-xxl, .container-xl, .container-lg, .container-md, .container-sm { width: 100%; padding-left: var(--bs-gutter-x, 0.75rem); padding-right: var(--bs-gutter-x, 0.75rem); margin-left: auto; margin-right: auto; } @media (min-width: 576px) { .container-sm, .container { max-width: 540px; } } @media (min-width: 768px) { .container-md, .container-sm, .container { max-width: 720px; } } @media (min-width: 992px) { .container-lg, .container-md, .container-sm, .container { max-width: 960px; } } @media (min-width: 1200px) { .container-xl, .container-lg, .container-md, .container-sm, .container { max-width: 1140px; } } @media (min-width: 1400px) { .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { max-width: 1320px; } } .row { --bs-gutter-x: 1.5rem; --bs-gutter-y: 0; display: flex; flex-wrap: wrap; margin-top: calc(-1 * var(--bs-gutter-y)); margin-left: calc(-0.5 * var(--bs-gutter-x)); margin-right: calc(-0.5 * var(--bs-gutter-x)); } .row > * { flex-shrink: 0; width: 100%; max-width: 100%; padding-left: calc(var(--bs-gutter-x) * 0.5); padding-right: calc(var(--bs-gutter-x) * 0.5); margin-top: var(--bs-gutter-y); } .col { flex: 1 0 0%; } .row-cols-auto > * { flex: 0 0 auto; width: auto; } .row-cols-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-auto { flex: 0 0 auto; width: auto; } .col-1 { flex: 0 0 auto; width: 8.33333333%; } .col-2 { flex: 0 0 auto; width: 16.66666667%; } .col-3 { flex: 0 0 auto; width: 25%; } .col-4 { flex: 0 0 auto; width: 33.33333333%; } .col-5 { flex: 0 0 auto; width: 41.66666667%; } .col-6 { flex: 0 0 auto; width: 50%; } .col-7 { flex: 0 0 auto; width: 58.33333333%; } .col-8 { flex: 0 0 auto; width: 66.66666667%; } .col-9 { flex: 0 0 auto; width: 75%; } .col-10 { flex: 0 0 auto; width: 83.33333333%; } .col-11 { flex: 0 0 auto; width: 91.66666667%; } .col-12 { flex: 0 0 auto; width: 100%; } .offset-1 { margin-right: 8.33333333%; } .offset-2 { margin-right: 16.66666667%; } .offset-3 { margin-right: 25%; } .offset-4 { margin-right: 33.33333333%; } .offset-5 { margin-right: 41.66666667%; } .offset-6 { margin-right: 50%; } .offset-7 { margin-right: 58.33333333%; } .offset-8 { margin-right: 66.66666667%; } .offset-9 { margin-right: 75%; } .offset-10 { margin-right: 83.33333333%; } .offset-11 { margin-right: 91.66666667%; } .g-0, .gx-0 { --bs-gutter-x: 0; } .g-0, .gy-0 { --bs-gutter-y: 0; } .g-1, .gx-1 { --bs-gutter-x: 0.25rem; } .g-1, .gy-1 { --bs-gutter-y: 0.25rem; } .g-2, .gx-2 { --bs-gutter-x: 0.5rem; } .g-2, .gy-2 { --bs-gutter-y: 0.5rem; } .g-3, .gx-3 { --bs-gutter-x: 1rem; } .g-3, .gy-3 { --bs-gutter-y: 1rem; } .g-4, .gx-4 { --bs-gutter-x: 1.5rem; } .g-4, .gy-4 { --bs-gutter-y: 1.5rem; } .g-5, .gx-5 { --bs-gutter-x: 3rem; } .g-5, .gy-5 { --bs-gutter-y: 3rem; } @media (min-width: 576px) { .col-sm { flex: 1 0 0%; } .row-cols-sm-auto > * { flex: 0 0 auto; width: auto; } .row-cols-sm-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-sm-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-sm-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-sm-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-sm-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-sm-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-sm-auto { flex: 0 0 auto; width: auto; } .col-sm-1 { flex: 0 0 auto; width: 8.33333333%; } .col-sm-2 { flex: 0 0 auto; width: 16.66666667%; } .col-sm-3 { flex: 0 0 auto; width: 25%; } .col-sm-4 { flex: 0 0 auto; width: 33.33333333%; } .col-sm-5 { flex: 0 0 auto; width: 41.66666667%; } .col-sm-6 { flex: 0 0 auto; width: 50%; } .col-sm-7 { flex: 0 0 auto; width: 58.33333333%; } .col-sm-8 { flex: 0 0 auto; width: 66.66666667%; } .col-sm-9 { flex: 0 0 auto; width: 75%; } .col-sm-10 { flex: 0 0 auto; width: 83.33333333%; } .col-sm-11 { flex: 0 0 auto; width: 91.66666667%; } .col-sm-12 { flex: 0 0 auto; width: 100%; } .offset-sm-0 { margin-right: 0; } .offset-sm-1 { margin-right: 8.33333333%; } .offset-sm-2 { margin-right: 16.66666667%; } .offset-sm-3 { margin-right: 25%; } .offset-sm-4 { margin-right: 33.33333333%; } .offset-sm-5 { margin-right: 41.66666667%; } .offset-sm-6 { margin-right: 50%; } .offset-sm-7 { margin-right: 58.33333333%; } .offset-sm-8 { margin-right: 66.66666667%; } .offset-sm-9 { margin-right: 75%; } .offset-sm-10 { margin-right: 83.33333333%; } .offset-sm-11 { margin-right: 91.66666667%; } .g-sm-0, .gx-sm-0 { --bs-gutter-x: 0; } .g-sm-0, .gy-sm-0 { --bs-gutter-y: 0; } .g-sm-1, .gx-sm-1 { --bs-gutter-x: 0.25rem; } .g-sm-1, .gy-sm-1 { --bs-gutter-y: 0.25rem; } .g-sm-2, .gx-sm-2 { --bs-gutter-x: 0.5rem; } .g-sm-2, .gy-sm-2 { --bs-gutter-y: 0.5rem; } .g-sm-3, .gx-sm-3 { --bs-gutter-x: 1rem; } .g-sm-3, .gy-sm-3 { --bs-gutter-y: 1rem; } .g-sm-4, .gx-sm-4 { --bs-gutter-x: 1.5rem; } .g-sm-4, .gy-sm-4 { --bs-gutter-y: 1.5rem; } .g-sm-5, .gx-sm-5 { --bs-gutter-x: 3rem; } .g-sm-5, .gy-sm-5 { --bs-gutter-y: 3rem; } } @media (min-width: 768px) { .col-md { flex: 1 0 0%; } .row-cols-md-auto > * { flex: 0 0 auto; width: auto; } .row-cols-md-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-md-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-md-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-md-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-md-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-md-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-md-auto { flex: 0 0 auto; width: auto; } .col-md-1 { flex: 0 0 auto; width: 8.33333333%; } .col-md-2 { flex: 0 0 auto; width: 16.66666667%; } .col-md-3 { flex: 0 0 auto; width: 25%; } .col-md-4 { flex: 0 0 auto; width: 33.33333333%; } .col-md-5 { flex: 0 0 auto; width: 41.66666667%; } .col-md-6 { flex: 0 0 auto; width: 50%; } .col-md-7 { flex: 0 0 auto; width: 58.33333333%; } .col-md-8 { flex: 0 0 auto; width: 66.66666667%; } .col-md-9 { flex: 0 0 auto; width: 75%; } .col-md-10 { flex: 0 0 auto; width: 83.33333333%; } .col-md-11 { flex: 0 0 auto; width: 91.66666667%; } .col-md-12 { flex: 0 0 auto; width: 100%; } .offset-md-0 { margin-right: 0; } .offset-md-1 { margin-right: 8.33333333%; } .offset-md-2 { margin-right: 16.66666667%; } .offset-md-3 { margin-right: 25%; } .offset-md-4 { margin-right: 33.33333333%; } .offset-md-5 { margin-right: 41.66666667%; } .offset-md-6 { margin-right: 50%; } .offset-md-7 { margin-right: 58.33333333%; } .offset-md-8 { margin-right: 66.66666667%; } .offset-md-9 { margin-right: 75%; } .offset-md-10 { margin-right: 83.33333333%; } .offset-md-11 { margin-right: 91.66666667%; } .g-md-0, .gx-md-0 { --bs-gutter-x: 0; } .g-md-0, .gy-md-0 { --bs-gutter-y: 0; } .g-md-1, .gx-md-1 { --bs-gutter-x: 0.25rem; } .g-md-1, .gy-md-1 { --bs-gutter-y: 0.25rem; } .g-md-2, .gx-md-2 { --bs-gutter-x: 0.5rem; } .g-md-2, .gy-md-2 { --bs-gutter-y: 0.5rem; } .g-md-3, .gx-md-3 { --bs-gutter-x: 1rem; } .g-md-3, .gy-md-3 { --bs-gutter-y: 1rem; } .g-md-4, .gx-md-4 { --bs-gutter-x: 1.5rem; } .g-md-4, .gy-md-4 { --bs-gutter-y: 1.5rem; } .g-md-5, .gx-md-5 { --bs-gutter-x: 3rem; } .g-md-5, .gy-md-5 { --bs-gutter-y: 3rem; } } @media (min-width: 992px) { .col-lg { flex: 1 0 0%; } .row-cols-lg-auto > * { flex: 0 0 auto; width: auto; } .row-cols-lg-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-lg-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-lg-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-lg-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-lg-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-lg-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-lg-auto { flex: 0 0 auto; width: auto; } .col-lg-1 { flex: 0 0 auto; width: 8.33333333%; } .col-lg-2 { flex: 0 0 auto; width: 16.66666667%; } .col-lg-3 { flex: 0 0 auto; width: 25%; } .col-lg-4 { flex: 0 0 auto; width: 33.33333333%; } .col-lg-5 { flex: 0 0 auto; width: 41.66666667%; } .col-lg-6 { flex: 0 0 auto; width: 50%; } .col-lg-7 { flex: 0 0 auto; width: 58.33333333%; } .col-lg-8 { flex: 0 0 auto; width: 66.66666667%; } .col-lg-9 { flex: 0 0 auto; width: 75%; } .col-lg-10 { flex: 0 0 auto; width: 83.33333333%; } .col-lg-11 { flex: 0 0 auto; width: 91.66666667%; } .col-lg-12 { flex: 0 0 auto; width: 100%; } .offset-lg-0 { margin-right: 0; } .offset-lg-1 { margin-right: 8.33333333%; } .offset-lg-2 { margin-right: 16.66666667%; } .offset-lg-3 { margin-right: 25%; } .offset-lg-4 { margin-right: 33.33333333%; } .offset-lg-5 { margin-right: 41.66666667%; } .offset-lg-6 { margin-right: 50%; } .offset-lg-7 { margin-right: 58.33333333%; } .offset-lg-8 { margin-right: 66.66666667%; } .offset-lg-9 { margin-right: 75%; } .offset-lg-10 { margin-right: 83.33333333%; } .offset-lg-11 { margin-right: 91.66666667%; } .g-lg-0, .gx-lg-0 { --bs-gutter-x: 0; } .g-lg-0, .gy-lg-0 { --bs-gutter-y: 0; } .g-lg-1, .gx-lg-1 { --bs-gutter-x: 0.25rem; } .g-lg-1, .gy-lg-1 { --bs-gutter-y: 0.25rem; } .g-lg-2, .gx-lg-2 { --bs-gutter-x: 0.5rem; } .g-lg-2, .gy-lg-2 { --bs-gutter-y: 0.5rem; } .g-lg-3, .gx-lg-3 { --bs-gutter-x: 1rem; } .g-lg-3, .gy-lg-3 { --bs-gutter-y: 1rem; } .g-lg-4, .gx-lg-4 { --bs-gutter-x: 1.5rem; } .g-lg-4, .gy-lg-4 { --bs-gutter-y: 1.5rem; } .g-lg-5, .gx-lg-5 { --bs-gutter-x: 3rem; } .g-lg-5, .gy-lg-5 { --bs-gutter-y: 3rem; } } @media (min-width: 1200px) { .col-xl { flex: 1 0 0%; } .row-cols-xl-auto > * { flex: 0 0 auto; width: auto; } .row-cols-xl-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-xl-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-xl-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-xl-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-xl-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-xl-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-xl-auto { flex: 0 0 auto; width: auto; } .col-xl-1 { flex: 0 0 auto; width: 8.33333333%; } .col-xl-2 { flex: 0 0 auto; width: 16.66666667%; } .col-xl-3 { flex: 0 0 auto; width: 25%; } .col-xl-4 { flex: 0 0 auto; width: 33.33333333%; } .col-xl-5 { flex: 0 0 auto; width: 41.66666667%; } .col-xl-6 { flex: 0 0 auto; width: 50%; } .col-xl-7 { flex: 0 0 auto; width: 58.33333333%; } .col-xl-8 { flex: 0 0 auto; width: 66.66666667%; } .col-xl-9 { flex: 0 0 auto; width: 75%; } .col-xl-10 { flex: 0 0 auto; width: 83.33333333%; } .col-xl-11 { flex: 0 0 auto; width: 91.66666667%; } .col-xl-12 { flex: 0 0 auto; width: 100%; } .offset-xl-0 { margin-right: 0; } .offset-xl-1 { margin-right: 8.33333333%; } .offset-xl-2 { margin-right: 16.66666667%; } .offset-xl-3 { margin-right: 25%; } .offset-xl-4 { margin-right: 33.33333333%; } .offset-xl-5 { margin-right: 41.66666667%; } .offset-xl-6 { margin-right: 50%; } .offset-xl-7 { margin-right: 58.33333333%; } .offset-xl-8 { margin-right: 66.66666667%; } .offset-xl-9 { margin-right: 75%; } .offset-xl-10 { margin-right: 83.33333333%; } .offset-xl-11 { margin-right: 91.66666667%; } .g-xl-0, .gx-xl-0 { --bs-gutter-x: 0; } .g-xl-0, .gy-xl-0 { --bs-gutter-y: 0; } .g-xl-1, .gx-xl-1 { --bs-gutter-x: 0.25rem; } .g-xl-1, .gy-xl-1 { --bs-gutter-y: 0.25rem; } .g-xl-2, .gx-xl-2 { --bs-gutter-x: 0.5rem; } .g-xl-2, .gy-xl-2 { --bs-gutter-y: 0.5rem; } .g-xl-3, .gx-xl-3 { --bs-gutter-x: 1rem; } .g-xl-3, .gy-xl-3 { --bs-gutter-y: 1rem; } .g-xl-4, .gx-xl-4 { --bs-gutter-x: 1.5rem; } .g-xl-4, .gy-xl-4 { --bs-gutter-y: 1.5rem; } .g-xl-5, .gx-xl-5 { --bs-gutter-x: 3rem; } .g-xl-5, .gy-xl-5 { --bs-gutter-y: 3rem; } } @media (min-width: 1400px) { .col-xxl { flex: 1 0 0%; } .row-cols-xxl-auto > * { flex: 0 0 auto; width: auto; } .row-cols-xxl-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-xxl-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-xxl-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-xxl-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-xxl-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-xxl-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-xxl-auto { flex: 0 0 auto; width: auto; } .col-xxl-1 { flex: 0 0 auto; width: 8.33333333%; } .col-xxl-2 { flex: 0 0 auto; width: 16.66666667%; } .col-xxl-3 { flex: 0 0 auto; width: 25%; } .col-xxl-4 { flex: 0 0 auto; width: 33.33333333%; } .col-xxl-5 { flex: 0 0 auto; width: 41.66666667%; } .col-xxl-6 { flex: 0 0 auto; width: 50%; } .col-xxl-7 { flex: 0 0 auto; width: 58.33333333%; } .col-xxl-8 { flex: 0 0 auto; width: 66.66666667%; } .col-xxl-9 { flex: 0 0 auto; width: 75%; } .col-xxl-10 { flex: 0 0 auto; width: 83.33333333%; } .col-xxl-11 { flex: 0 0 auto; width: 91.66666667%; } .col-xxl-12 { flex: 0 0 auto; width: 100%; } .offset-xxl-0 { margin-right: 0; } .offset-xxl-1 { margin-right: 8.33333333%; } .offset-xxl-2 { margin-right: 16.66666667%; } .offset-xxl-3 { margin-right: 25%; } .offset-xxl-4 { margin-right: 33.33333333%; } .offset-xxl-5 { margin-right: 41.66666667%; } .offset-xxl-6 { margin-right: 50%; } .offset-xxl-7 { margin-right: 58.33333333%; } .offset-xxl-8 { margin-right: 66.66666667%; } .offset-xxl-9 { margin-right: 75%; } .offset-xxl-10 { margin-right: 83.33333333%; } .offset-xxl-11 { margin-right: 91.66666667%; } .g-xxl-0, .gx-xxl-0 { --bs-gutter-x: 0; } .g-xxl-0, .gy-xxl-0 { --bs-gutter-y: 0; } .g-xxl-1, .gx-xxl-1 { --bs-gutter-x: 0.25rem; } .g-xxl-1, .gy-xxl-1 { --bs-gutter-y: 0.25rem; } .g-xxl-2, .gx-xxl-2 { --bs-gutter-x: 0.5rem; } .g-xxl-2, .gy-xxl-2 { --bs-gutter-y: 0.5rem; } .g-xxl-3, .gx-xxl-3 { --bs-gutter-x: 1rem; } .g-xxl-3, .gy-xxl-3 { --bs-gutter-y: 1rem; } .g-xxl-4, .gx-xxl-4 { --bs-gutter-x: 1.5rem; } .g-xxl-4, .gy-xxl-4 { --bs-gutter-y: 1.5rem; } .g-xxl-5, .gx-xxl-5 { --bs-gutter-x: 3rem; } .g-xxl-5, .gy-xxl-5 { --bs-gutter-y: 3rem; } } .table { --bs-table-bg: transparent; --bs-table-accent-bg: transparent; --bs-table-striped-color: #212529; --bs-table-striped-bg: rgba(0, 0, 0, 0.05); --bs-table-active-color: #212529; --bs-table-active-bg: rgba(0, 0, 0, 0.1); --bs-table-hover-color: #212529; --bs-table-hover-bg: rgba(0, 0, 0, 0.075); width: 100%; margin-bottom: 1rem; color: #212529; vertical-align: top; border-color: #dee2e6; } .table > :not(caption) > * > * { padding: 0.5rem 0.5rem; background-color: var(--bs-table-bg); border-bottom-width: 1px; box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg); } .table > tbody { vertical-align: inherit; } .table > thead { vertical-align: bottom; } .table > :not(:first-child) { border-top: 2px solid currentColor; } .caption-top { caption-side: top; } .table-sm > :not(caption) > * > * { padding: 0.25rem 0.25rem; } .table-bordered > :not(caption) > * { border-width: 1px 0; } .table-bordered > :not(caption) > * > * { border-width: 0 1px; } .table-borderless > :not(caption) > * > * { border-bottom-width: 0; } .table-borderless > :not(:first-child) { border-top-width: 0; } .table-striped > tbody > tr:nth-of-type(odd) > * { --bs-table-accent-bg: var(--bs-table-striped-bg); color: var(--bs-table-striped-color); } .table-active { --bs-table-accent-bg: var(--bs-table-active-bg); color: var(--bs-table-active-color); } .table-hover > tbody > tr:hover > * { --bs-table-accent-bg: var(--bs-table-hover-bg); color: var(--bs-table-hover-color); } .table-primary { --bs-table-bg: #cfe2ff; --bs-table-striped-bg: #c5d7f2; --bs-table-striped-color: #000; --bs-table-active-bg: #bacbe6; --bs-table-active-color: #000; --bs-table-hover-bg: #bfd1ec; --bs-table-hover-color: #000; color: #000; border-color: #bacbe6; } .table-secondary { --bs-table-bg: #e2e3e5; --bs-table-striped-bg: #d7d8da; --bs-table-striped-color: #000; --bs-table-active-bg: #cbccce; --bs-table-active-color: #000; --bs-table-hover-bg: #d1d2d4; --bs-table-hover-color: #000; color: #000; border-color: #cbccce; } .table-success { --bs-table-bg: #d1e7dd; --bs-table-striped-bg: #c7dbd2; --bs-table-striped-color: #000; --bs-table-active-bg: #bcd0c7; --bs-table-active-color: #000; --bs-table-hover-bg: #c1d6cc; --bs-table-hover-color: #000; color: #000; border-color: #bcd0c7; } .table-info { --bs-table-bg: #cff4fc; --bs-table-striped-bg: #c5e8ef; --bs-table-striped-color: #000; --bs-table-active-bg: #badce3; --bs-table-active-color: #000; --bs-table-hover-bg: #bfe2e9; --bs-table-hover-color: #000; color: #000; border-color: #badce3; } .table-warning { --bs-table-bg: #fff3cd; --bs-table-striped-bg: #f2e7c3; --bs-table-striped-color: #000; --bs-table-active-bg: #e6dbb9; --bs-table-active-color: #000; --bs-table-hover-bg: #ece1be; --bs-table-hover-color: #000; color: #000; border-color: #e6dbb9; } .table-danger { --bs-table-bg: #f8d7da; --bs-table-striped-bg: #eccccf; --bs-table-striped-color: #000; --bs-table-active-bg: #dfc2c4; --bs-table-active-color: #000; --bs-table-hover-bg: #e5c7ca; --bs-table-hover-color: #000; color: #000; border-color: #dfc2c4; } .table-light { --bs-table-bg: #f8f9fa; --bs-table-striped-bg: #ecedee; --bs-table-striped-color: #000; --bs-table-active-bg: #dfe0e1; --bs-table-active-color: #000; --bs-table-hover-bg: #e5e6e7; --bs-table-hover-color: #000; color: #000; border-color: #dfe0e1; } .table-dark { --bs-table-bg: #212529; --bs-table-striped-bg: #2c3034; --bs-table-striped-color: #fff; --bs-table-active-bg: #373b3e; --bs-table-active-color: #fff; --bs-table-hover-bg: #323539; --bs-table-hover-color: #fff; color: #fff; border-color: #373b3e; } .table-responsive { overflow-x: auto; -webkit-overflow-scrolling: touch; } @media (max-width: 575.98px) { .table-responsive-sm { overflow-x: auto; -webkit-overflow-scrolling: touch; } } @media (max-width: 767.98px) { .table-responsive-md { overflow-x: auto; -webkit-overflow-scrolling: touch; } } @media (max-width: 991.98px) { .table-responsive-lg { overflow-x: auto; -webkit-overflow-scrolling: touch; } } @media (max-width: 1199.98px) { .table-responsive-xl { overflow-x: auto; -webkit-overflow-scrolling: touch; } } @media (max-width: 1399.98px) { .table-responsive-xxl { overflow-x: auto; -webkit-overflow-scrolling: touch; } } .form-label { margin-bottom: 0.5rem; } .col-form-label { padding-top: calc(0.375rem + 1px); padding-bottom: calc(0.375rem + 1px); margin-bottom: 0; font-size: inherit; line-height: 1.5; } .col-form-label-lg { padding-top: calc(0.5rem + 1px); padding-bottom: calc(0.5rem + 1px); font-size: 1.25rem; } .col-form-label-sm { padding-top: calc(0.25rem + 1px); padding-bottom: calc(0.25rem + 1px); font-size: 0.875rem; } .form-text { margin-top: 0.25rem; font-size: 0.875em; color: #6c757d; } .form-control { display: block; width: 100%; padding: 0.375rem 0.75rem; font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; background-color: #fff; background-clip: padding-box; border: 1px solid #ced4da; -webkit-appearance: none; -moz-appearance: none; appearance: none; border-radius: 0.25rem; transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-control { transition: none; } } .form-control[type="file"] { overflow: hidden; } .form-control[type="file"]:not(:disabled):not([readonly]) { cursor: pointer; } .form-control:focus { color: #212529; background-color: #fff; border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-control::-webkit-date-and-time-value { height: 1.5em; } .form-control::-moz-placeholder { color: #6c757d; opacity: 1; } .form-control::placeholder { color: #6c757d; opacity: 1; } .form-control:disabled, .form-control[readonly] { background-color: #e9ecef; opacity: 1; } .form-control::-webkit-file-upload-button { padding: 0.375rem 0.75rem; margin: -0.375rem -0.75rem; -webkit-margin-end: 0.75rem; margin-inline-end: 0.75rem; color: #212529; background-color: #e9ecef; pointer-events: none; border-color: inherit; border-style: solid; border-width: 0; border-inline-end-width: 1px; border-radius: 0; -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } .form-control::file-selector-button { padding: 0.375rem 0.75rem; margin: -0.375rem -0.75rem; -webkit-margin-end: 0.75rem; margin-inline-end: 0.75rem; color: #212529; background-color: #e9ecef; pointer-events: none; border-color: inherit; border-style: solid; border-width: 0; border-inline-end-width: 1px; border-radius: 0; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-control::-webkit-file-upload-button { -webkit-transition: none; transition: none; } .form-control::file-selector-button { transition: none; } } .form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button { background-color: #dde0e3; } .form-control:hover:not(:disabled):not([readonly])::file-selector-button { background-color: #dde0e3; } .form-control::-webkit-file-upload-button { padding: 0.375rem 0.75rem; margin: -0.375rem -0.75rem; -webkit-margin-end: 0.75rem; margin-inline-end: 0.75rem; color: #212529; background-color: #e9ecef; pointer-events: none; border-color: inherit; border-style: solid; border-width: 0; border-inline-end-width: 1px; border-radius: 0; -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-control::-webkit-file-upload-button { -webkit-transition: none; transition: none; } } .form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button { background-color: #dde0e3; } .form-control-plaintext { display: block; width: 100%; padding: 0.375rem 0; margin-bottom: 0; line-height: 1.5; color: #212529; background-color: transparent; border: solid transparent; border-width: 1px 0; } .form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { padding-left: 0; padding-right: 0; } .form-control-sm { min-height: calc(1.5em + 0.5rem + 2px); padding: 0.25rem 0.5rem; font-size: 0.875rem; border-radius: 0.2rem; } .form-control-sm::-webkit-file-upload-button { padding: 0.25rem 0.5rem; margin: -0.25rem -0.5rem; -webkit-margin-end: 0.5rem; margin-inline-end: 0.5rem; } .form-control-sm::file-selector-button { padding: 0.25rem 0.5rem; margin: -0.25rem -0.5rem; -webkit-margin-end: 0.5rem; margin-inline-end: 0.5rem; } .form-control-sm::-webkit-file-upload-button { padding: 0.25rem 0.5rem; margin: -0.25rem -0.5rem; -webkit-margin-end: 0.5rem; margin-inline-end: 0.5rem; } .form-control-lg { min-height: calc(1.5em + 1rem + 2px); padding: 0.5rem 1rem; font-size: 1.25rem; border-radius: 0.3rem; } .form-control-lg::-webkit-file-upload-button { padding: 0.5rem 1rem; margin: -0.5rem -1rem; -webkit-margin-end: 1rem; margin-inline-end: 1rem; } .form-control-lg::file-selector-button { padding: 0.5rem 1rem; margin: -0.5rem -1rem; -webkit-margin-end: 1rem; margin-inline-end: 1rem; } .form-control-lg::-webkit-file-upload-button { padding: 0.5rem 1rem; margin: -0.5rem -1rem; -webkit-margin-end: 1rem; margin-inline-end: 1rem; } textarea.form-control { min-height: calc(1.5em + 0.75rem + 2px); } textarea.form-control-sm { min-height: calc(1.5em + 0.5rem + 2px); } textarea.form-control-lg { min-height: calc(1.5em + 1rem + 2px); } .form-control-color { width: 3rem; height: auto; padding: 0.375rem; } .form-control-color:not(:disabled):not([readonly]) { cursor: pointer; } .form-control-color::-moz-color-swatch { height: 1.5em; border-radius: 0.25rem; } .form-control-color::-webkit-color-swatch { height: 1.5em; border-radius: 0.25rem; } .form-select { display: block; width: 100%; padding: 0.375rem 0.75rem 0.375rem 2.25rem; -moz-padding-start: calc(0.75rem - 3px); font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; background-color: #fff; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: left 0.75rem center; background-size: 16px 12px; border: 1px solid #ced4da; border-radius: 0.25rem; transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -webkit-appearance: none; -moz-appearance: none; appearance: none; } @media (prefers-reduced-motion: reduce) { .form-select { transition: none; } } .form-select:focus { border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-select[multiple], .form-select[size]:not([size="1"]) { padding-left: 0.75rem; background-image: none; } .form-select:disabled { background-color: #e9ecef; } .form-select:-moz-focusring { color: transparent; text-shadow: 0 0 0 #212529; } .form-select-sm { padding-top: 0.25rem; padding-bottom: 0.25rem; padding-right: 0.5rem; font-size: 0.875rem; border-radius: 0.2rem; } .form-select-lg { padding-top: 0.5rem; padding-bottom: 0.5rem; padding-right: 1rem; font-size: 1.25rem; border-radius: 0.3rem; } .form-check { display: block; min-height: 1.5rem; padding-right: 1.5em; margin-bottom: 0.125rem; } .form-check .form-check-input { float: right; margin-right: -1.5em; } .form-check-input { width: 1em; height: 1em; margin-top: 0.25em; vertical-align: top; background-color: #fff; background-repeat: no-repeat; background-position: center; background-size: contain; border: 1px solid rgba(0, 0, 0, 0.25); -webkit-appearance: none; -moz-appearance: none; appearance: none; -webkit-print-color-adjust: exact; color-adjust: exact; } .form-check-input[type="checkbox"] { border-radius: 0.25em; } .form-check-input[type="radio"] { border-radius: 50%; } .form-check-input:active { filter: brightness(90%); } .form-check-input:focus { border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-check-input:checked { background-color: #0d6efd; border-color: #0d6efd; } .form-check-input:checked[type="checkbox"] { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e"); } .form-check-input:checked[type="radio"] { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); } .form-check-input[type="checkbox"]:indeterminate { background-color: #0d6efd; border-color: #0d6efd; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"); } .form-check-input:disabled { pointer-events: none; filter: none; opacity: 0.5; } .form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { opacity: 0.5; } .form-switch { padding-right: 2.5em; } .form-switch .form-check-input { width: 2em; margin-right: -2.5em; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); background-position: right center; border-radius: 2em; transition: background-position 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-switch .form-check-input { transition: none; } } .form-switch .form-check-input:focus { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e"); } .form-switch .form-check-input:checked { background-position: left center; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } .form-check-inline { display: inline-block; margin-left: 1rem; } .btn-check { position: absolute; clip: rect(0, 0, 0, 0); pointer-events: none; } .btn-check[disabled] + .btn, .btn-check:disabled + .btn { pointer-events: none; filter: none; opacity: 0.65; } .form-range { width: 100%; height: 1.5rem; padding: 0; background-color: transparent; -webkit-appearance: none; -moz-appearance: none; appearance: none; } .form-range:focus { outline: 0; } .form-range:focus::-webkit-slider-thumb { box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-range:focus::-moz-range-thumb { box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-range::-moz-focus-outer { border: 0; } .form-range::-webkit-slider-thumb { width: 1rem; height: 1rem; margin-top: -0.25rem; background-color: #0d6efd; border: 0; border-radius: 1rem; -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -webkit-appearance: none; appearance: none; } @media (prefers-reduced-motion: reduce) { .form-range::-webkit-slider-thumb { -webkit-transition: none; transition: none; } } .form-range::-webkit-slider-thumb:active { background-color: #b6d4fe; } .form-range::-webkit-slider-runnable-track { width: 100%; height: 0.5rem; color: transparent; cursor: pointer; background-color: #dee2e6; border-color: transparent; border-radius: 1rem; } .form-range::-moz-range-thumb { width: 1rem; height: 1rem; background-color: #0d6efd; border: 0; border-radius: 1rem; -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -moz-appearance: none; appearance: none; } @media (prefers-reduced-motion: reduce) { .form-range::-moz-range-thumb { -moz-transition: none; transition: none; } } .form-range::-moz-range-thumb:active { background-color: #b6d4fe; } .form-range::-moz-range-track { width: 100%; height: 0.5rem; color: transparent; cursor: pointer; background-color: #dee2e6; border-color: transparent; border-radius: 1rem; } .form-range:disabled { pointer-events: none; } .form-range:disabled::-webkit-slider-thumb { background-color: #adb5bd; } .form-range:disabled::-moz-range-thumb { background-color: #adb5bd; } .form-floating { position: relative; } .form-floating > .form-control, .form-floating > .form-select { height: calc(3.5rem + 2px); line-height: 1.25; } .form-floating > label { position: absolute; top: 0; right: 0; height: 100%; padding: 1rem 0.75rem; pointer-events: none; border: 1px solid transparent; transform-origin: 100% 0; transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-floating > label { transition: none; } } .form-floating > .form-control { padding: 1rem 0.75rem; } .form-floating > .form-control::-moz-placeholder { color: transparent; } .form-floating > .form-control::placeholder { color: transparent; } .form-floating > .form-control:not(:-moz-placeholder-shown) { padding-top: 1.625rem; padding-bottom: 0.625rem; } .form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown) { padding-top: 1.625rem; padding-bottom: 0.625rem; } .form-floating > .form-control:-webkit-autofill { padding-top: 1.625rem; padding-bottom: 0.625rem; } .form-floating > .form-select { padding-top: 1.625rem; padding-bottom: 0.625rem; } .form-floating > .form-control:not(:-moz-placeholder-shown) ~ label { opacity: 0.65; transform: scale(0.85) translateY(-0.5rem) translateX(-0.15rem); } .form-floating > .form-control:focus ~ label, .form-floating > .form-control:not(:placeholder-shown) ~ label, .form-floating > .form-select ~ label { opacity: 0.65; transform: scale(0.85) translateY(-0.5rem) translateX(-0.15rem); } .form-floating > .form-control:-webkit-autofill ~ label { opacity: 0.65; transform: scale(0.85) translateY(-0.5rem) translateX(-0.15rem); } .input-group { position: relative; display: flex; flex-wrap: wrap; align-items: stretch; width: 100%; } .input-group > .form-control, .input-group > .form-select { position: relative; flex: 1 1 auto; width: 1%; min-width: 0; } .input-group > .form-control:focus, .input-group > .form-select:focus { z-index: 3; } .input-group .btn { position: relative; z-index: 2; } .input-group .btn:focus { z-index: 3; } .input-group-text { display: flex; align-items: center; padding: 0.375rem 0.75rem; font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; text-align: center; white-space: nowrap; background-color: #e9ecef; border: 1px solid #ced4da; border-radius: 0.25rem; } .input-group-lg > .form-control, .input-group-lg > .form-select, .input-group-lg > .input-group-text, .input-group-lg > .btn { padding: 0.5rem 1rem; font-size: 1.25rem; border-radius: 0.3rem; } .input-group-sm > .form-control, .input-group-sm > .form-select, .input-group-sm > .input-group-text, .input-group-sm > .btn { padding: 0.25rem 0.5rem; font-size: 0.875rem; border-radius: 0.2rem; } .input-group-lg > .form-select, .input-group-sm > .form-select { padding-left: 3rem; } .input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu), .input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n + 3) { border-top-left-radius: 0; border-bottom-left-radius: 0; } .input-group.has-validation > :nth-last-child(n + 3):not(.dropdown-toggle):not(.dropdown-menu), .input-group.has-validation > .dropdown-toggle:nth-last-child(n + 4) { border-top-left-radius: 0; border-bottom-left-radius: 0; } .input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) { margin-right: -1px; border-top-right-radius: 0; border-bottom-right-radius: 0; } .valid-feedback { display: none; width: 100%; margin-top: 0.25rem; font-size: 0.875em; color: #198754; } .valid-tooltip { position: absolute; top: 100%; z-index: 5; display: none; max-width: 100%; padding: 0.25rem 0.5rem; margin-top: 0.1rem; font-size: 0.875rem; color: #fff; background-color: rgba(25, 135, 84, 0.9); border-radius: 0.25rem; } .was-validated :valid ~ .valid-feedback, .was-validated :valid ~ .valid-tooltip, .is-valid ~ .valid-feedback, .is-valid ~ .valid-tooltip { display: block; } .was-validated .form-control:valid, .form-control.is-valid { border-color: #198754; padding-left: calc(1.5em + 0.75rem); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: left calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:valid:focus, .form-control.is-valid:focus { border-color: #198754; box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); } .was-validated textarea.form-control:valid, textarea.form-control.is-valid { padding-left: calc(1.5em + 0.75rem); background-position: top calc(0.375em + 0.1875rem) left calc(0.375em + 0.1875rem); } .was-validated .form-select:valid, .form-select.is-valid { border-color: #198754; } .was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size="1"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size="1"] { padding-left: 4.125rem; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); background-position: left 0.75rem center, center left 2.25rem; background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-select:valid:focus, .form-select.is-valid:focus { border-color: #198754; box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); } .was-validated .form-check-input:valid, .form-check-input.is-valid { border-color: #198754; } .was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked { background-color: #198754; } .was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus { box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); } .was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { color: #198754; } .form-check-inline .form-check-input ~ .valid-feedback { margin-right: 0.5em; } .was-validated .input-group .form-control:valid, .input-group .form-control.is-valid, .was-validated .input-group .form-select:valid, .input-group .form-select.is-valid { z-index: 1; } .was-validated .input-group .form-control:valid:focus, .input-group .form-control.is-valid:focus, .was-validated .input-group .form-select:valid:focus, .input-group .form-select.is-valid:focus { z-index: 3; } .invalid-feedback { display: none; width: 100%; margin-top: 0.25rem; font-size: 0.875em; color: #dc3545; } .invalid-tooltip { position: absolute; top: 100%; z-index: 5; display: none; max-width: 100%; padding: 0.25rem 0.5rem; margin-top: 0.1rem; font-size: 0.875rem; color: #fff; background-color: rgba(220, 53, 69, 0.9); border-radius: 0.25rem; } .was-validated :invalid ~ .invalid-feedback, .was-validated :invalid ~ .invalid-tooltip, .is-invalid ~ .invalid-feedback, .is-invalid ~ .invalid-tooltip { display: block; } .was-validated .form-control:invalid, .form-control.is-invalid { border-color: #dc3545; padding-left: calc(1.5em + 0.75rem); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: left calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); } .was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { padding-left: calc(1.5em + 0.75rem); background-position: top calc(0.375em + 0.1875rem) left calc(0.375em + 0.1875rem); } .was-validated .form-select:invalid, .form-select.is-invalid { border-color: #dc3545; } .was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size="1"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size="1"] { padding-left: 4.125rem; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-position: left 0.75rem center, center left 2.25rem; background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-select:invalid:focus, .form-select.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); } .was-validated .form-check-input:invalid, .form-check-input.is-invalid { border-color: #dc3545; } .was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked { background-color: #dc3545; } .was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus { box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); } .was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { color: #dc3545; } .form-check-inline .form-check-input ~ .invalid-feedback { margin-right: 0.5em; } .was-validated .input-group .form-control:invalid, .input-group .form-control.is-invalid, .was-validated .input-group .form-select:invalid, .input-group .form-select.is-invalid { z-index: 2; } .was-validated .input-group .form-control:invalid:focus, .input-group .form-control.is-invalid:focus, .was-validated .input-group .form-select:invalid:focus, .input-group .form-select.is-invalid:focus { z-index: 3; } .btn { display: inline-block; font-weight: 400; line-height: 1.5; color: #212529; text-align: center; text-decoration: none; vertical-align: middle; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; user-select: none; background-color: transparent; border: 1px solid transparent; padding: 0.375rem 0.75rem; font-size: 1rem; border-radius: 0.25rem; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .btn { transition: none; } } .btn:hover { color: #212529; } .btn-check:focus + .btn, .btn:focus { outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .btn:disabled, .btn.disabled, fieldset:disabled .btn { pointer-events: none; opacity: 0.65; } .btn-primary { color: #fff; background-color: #0d6efd; border-color: #0d6efd; } .btn-primary:hover { color: #fff; background-color: #0b5ed7; border-color: #0a58ca; } .btn-check:focus + .btn-primary, .btn-primary:focus { color: #fff; background-color: #0b5ed7; border-color: #0a58ca; box-shadow: 0 0 0 0.25rem rgba(49, 132, 253, 0.5); } .btn-check:checked + .btn-primary, .btn-check:active + .btn-primary, .btn-primary:active, .btn-primary.active, .show > .btn-primary.dropdown-toggle { color: #fff; background-color: #0a58ca; border-color: #0a53be; } .btn-check:checked + .btn-primary:focus, .btn-check:active + .btn-primary:focus, .btn-primary:active:focus, .btn-primary.active:focus, .show > .btn-primary.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(49, 132, 253, 0.5); } .btn-primary:disabled, .btn-primary.disabled { color: #fff; background-color: #0d6efd; border-color: #0d6efd; } .btn-secondary { color: #fff; background-color: #6c757d; border-color: #6c757d; } .btn-secondary:hover { color: #fff; background-color: #5c636a; border-color: #565e64; } .btn-check:focus + .btn-secondary, .btn-secondary:focus { color: #fff; background-color: #5c636a; border-color: #565e64; box-shadow: 0 0 0 0.25rem rgba(130, 138, 145, 0.5); } .btn-check:checked + .btn-secondary, .btn-check:active + .btn-secondary, .btn-secondary:active, .btn-secondary.active, .show > .btn-secondary.dropdown-toggle { color: #fff; background-color: #565e64; border-color: #51585e; } .btn-check:checked + .btn-secondary:focus, .btn-check:active + .btn-secondary:focus, .btn-secondary:active:focus, .btn-secondary.active:focus, .show > .btn-secondary.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(130, 138, 145, 0.5); } .btn-secondary:disabled, .btn-secondary.disabled { color: #fff; background-color: #6c757d; border-color: #6c757d; } .btn-success { color: #fff; background-color: #198754; border-color: #198754; } .btn-success:hover { color: #fff; background-color: #157347; border-color: #146c43; } .btn-check:focus + .btn-success, .btn-success:focus { color: #fff; background-color: #157347; border-color: #146c43; box-shadow: 0 0 0 0.25rem rgba(60, 153, 110, 0.5); } .btn-check:checked + .btn-success, .btn-check:active + .btn-success, .btn-success:active, .btn-success.active, .show > .btn-success.dropdown-toggle { color: #fff; background-color: #146c43; border-color: #13653f; } .btn-check:checked + .btn-success:focus, .btn-check:active + .btn-success:focus, .btn-success:active:focus, .btn-success.active:focus, .show > .btn-success.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(60, 153, 110, 0.5); } .btn-success:disabled, .btn-success.disabled { color: #fff; background-color: #198754; border-color: #198754; } .btn-info { color: #000; background-color: #0dcaf0; border-color: #0dcaf0; } .btn-info:hover { color: #000; background-color: #31d2f2; border-color: #25cff2; } .btn-check:focus + .btn-info, .btn-info:focus { color: #000; background-color: #31d2f2; border-color: #25cff2; box-shadow: 0 0 0 0.25rem rgba(11, 172, 204, 0.5); } .btn-check:checked + .btn-info, .btn-check:active + .btn-info, .btn-info:active, .btn-info.active, .show > .btn-info.dropdown-toggle { color: #000; background-color: #3dd5f3; border-color: #25cff2; } .btn-check:checked + .btn-info:focus, .btn-check:active + .btn-info:focus, .btn-info:active:focus, .btn-info.active:focus, .show > .btn-info.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(11, 172, 204, 0.5); } .btn-info:disabled, .btn-info.disabled { color: #000; background-color: #0dcaf0; border-color: #0dcaf0; } .btn-warning { color: #000; background-color: #ffc107; border-color: #ffc107; } .btn-warning:hover { color: #000; background-color: #ffca2c; border-color: #ffc720; } .btn-check:focus + .btn-warning, .btn-warning:focus { color: #000; background-color: #ffca2c; border-color: #ffc720; box-shadow: 0 0 0 0.25rem rgba(217, 164, 6, 0.5); } .btn-check:checked + .btn-warning, .btn-check:active + .btn-warning, .btn-warning:active, .btn-warning.active, .show > .btn-warning.dropdown-toggle { color: #000; background-color: #ffcd39; border-color: #ffc720; } .btn-check:checked + .btn-warning:focus, .btn-check:active + .btn-warning:focus, .btn-warning:active:focus, .btn-warning.active:focus, .show > .btn-warning.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(217, 164, 6, 0.5); } .btn-warning:disabled, .btn-warning.disabled { color: #000; background-color: #ffc107; border-color: #ffc107; } .btn-danger { color: #fff; background-color: #dc3545; border-color: #dc3545; } .btn-danger:hover { color: #fff; background-color: #bb2d3b; border-color: #b02a37; } .btn-check:focus + .btn-danger, .btn-danger:focus { color: #fff; background-color: #bb2d3b; border-color: #b02a37; box-shadow: 0 0 0 0.25rem rgba(225, 83, 97, 0.5); } .btn-check:checked + .btn-danger, .btn-check:active + .btn-danger, .btn-danger:active, .btn-danger.active, .show > .btn-danger.dropdown-toggle { color: #fff; background-color: #b02a37; border-color: #a52834; } .btn-check:checked + .btn-danger:focus, .btn-check:active + .btn-danger:focus, .btn-danger:active:focus, .btn-danger.active:focus, .show > .btn-danger.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(225, 83, 97, 0.5); } .btn-danger:disabled, .btn-danger.disabled { color: #fff; background-color: #dc3545; border-color: #dc3545; } .btn-light { color: #000; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-light:hover { color: #000; background-color: #f9fafb; border-color: #f9fafb; } .btn-check:focus + .btn-light, .btn-light:focus { color: #000; background-color: #f9fafb; border-color: #f9fafb; box-shadow: 0 0 0 0.25rem rgba(211, 212, 213, 0.5); } .btn-check:checked + .btn-light, .btn-check:active + .btn-light, .btn-light:active, .btn-light.active, .show > .btn-light.dropdown-toggle { color: #000; background-color: #f9fafb; border-color: #f9fafb; } .btn-check:checked + .btn-light:focus, .btn-check:active + .btn-light:focus, .btn-light:active:focus, .btn-light.active:focus, .show > .btn-light.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(211, 212, 213, 0.5); } .btn-light:disabled, .btn-light.disabled { color: #000; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-dark { color: #fff; background-color: #212529; border-color: #212529; } .btn-dark:hover { color: #fff; background-color: #1c1f23; border-color: #1a1e21; } .btn-check:focus + .btn-dark, .btn-dark:focus { color: #fff; background-color: #1c1f23; border-color: #1a1e21; box-shadow: 0 0 0 0.25rem rgba(66, 70, 73, 0.5); } .btn-check:checked + .btn-dark, .btn-check:active + .btn-dark, .btn-dark:active, .btn-dark.active, .show > .btn-dark.dropdown-toggle { color: #fff; background-color: #1a1e21; border-color: #191c1f; } .btn-check:checked + .btn-dark:focus, .btn-check:active + .btn-dark:focus, .btn-dark:active:focus, .btn-dark.active:focus, .show > .btn-dark.dropdown-toggle:focus { box-shadow: 0 0 0 0.25rem rgba(66, 70, 73, 0.5); } .btn-dark:disabled, .btn-dark.disabled { color: #fff; background-color: #212529; border-color: #212529; } .btn-outline-primary { color: #0d6efd; border-color: #0d6efd; } .btn-outline-primary:hover { color: #fff; background-color: #0d6efd; border-color: #0d6efd; } .btn-check:focus + .btn-outline-primary, .btn-outline-primary:focus { box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.5); } .btn-check:checked + .btn-outline-primary, .btn-check:active + .btn-outline-primary, .btn-outline-primary:active, .btn-outline-primary.active, .btn-outline-primary.dropdown-toggle.show { color: #fff; background-color: #0d6efd; border-color: #0d6efd; } .btn-check:checked + .btn-outline-primary:focus, .btn-check:active + .btn-outline-primary:focus, .btn-outline-primary:active:focus, .btn-outline-primary.active:focus, .btn-outline-primary.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.5); } .btn-outline-primary:disabled, .btn-outline-primary.disabled { color: #0d6efd; background-color: transparent; } .btn-outline-secondary { color: #6c757d; border-color: #6c757d; } .btn-outline-secondary:hover { color: #fff; background-color: #6c757d; border-color: #6c757d; } .btn-check:focus + .btn-outline-secondary, .btn-outline-secondary:focus { box-shadow: 0 0 0 0.25rem rgba(108, 117, 125, 0.5); } .btn-check:checked + .btn-outline-secondary, .btn-check:active + .btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show { color: #fff; background-color: #6c757d; border-color: #6c757d; } .btn-check:checked + .btn-outline-secondary:focus, .btn-check:active + .btn-outline-secondary:focus, .btn-outline-secondary:active:focus, .btn-outline-secondary.active:focus, .btn-outline-secondary.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(108, 117, 125, 0.5); } .btn-outline-secondary:disabled, .btn-outline-secondary.disabled { color: #6c757d; background-color: transparent; } .btn-outline-success { color: #198754; border-color: #198754; } .btn-outline-success:hover { color: #fff; background-color: #198754; border-color: #198754; } .btn-check:focus + .btn-outline-success, .btn-outline-success:focus { box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.5); } .btn-check:checked + .btn-outline-success, .btn-check:active + .btn-outline-success, .btn-outline-success:active, .btn-outline-success.active, .btn-outline-success.dropdown-toggle.show { color: #fff; background-color: #198754; border-color: #198754; } .btn-check:checked + .btn-outline-success:focus, .btn-check:active + .btn-outline-success:focus, .btn-outline-success:active:focus, .btn-outline-success.active:focus, .btn-outline-success.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.5); } .btn-outline-success:disabled, .btn-outline-success.disabled { color: #198754; background-color: transparent; } .btn-outline-info { color: #0dcaf0; border-color: #0dcaf0; } .btn-outline-info:hover { color: #000; background-color: #0dcaf0; border-color: #0dcaf0; } .btn-check:focus + .btn-outline-info, .btn-outline-info:focus { box-shadow: 0 0 0 0.25rem rgba(13, 202, 240, 0.5); } .btn-check:checked + .btn-outline-info, .btn-check:active + .btn-outline-info, .btn-outline-info:active, .btn-outline-info.active, .btn-outline-info.dropdown-toggle.show { color: #000; background-color: #0dcaf0; border-color: #0dcaf0; } .btn-check:checked + .btn-outline-info:focus, .btn-check:active + .btn-outline-info:focus, .btn-outline-info:active:focus, .btn-outline-info.active:focus, .btn-outline-info.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(13, 202, 240, 0.5); } .btn-outline-info:disabled, .btn-outline-info.disabled { color: #0dcaf0; background-color: transparent; } .btn-outline-warning { color: #ffc107; border-color: #ffc107; } .btn-outline-warning:hover { color: #000; background-color: #ffc107; border-color: #ffc107; } .btn-check:focus + .btn-outline-warning, .btn-outline-warning:focus { box-shadow: 0 0 0 0.25rem rgba(255, 193, 7, 0.5); } .btn-check:checked + .btn-outline-warning, .btn-check:active + .btn-outline-warning, .btn-outline-warning:active, .btn-outline-warning.active, .btn-outline-warning.dropdown-toggle.show { color: #000; background-color: #ffc107; border-color: #ffc107; } .btn-check:checked + .btn-outline-warning:focus, .btn-check:active + .btn-outline-warning:focus, .btn-outline-warning:active:focus, .btn-outline-warning.active:focus, .btn-outline-warning.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(255, 193, 7, 0.5); } .btn-outline-warning:disabled, .btn-outline-warning.disabled { color: #ffc107; background-color: transparent; } .btn-outline-danger { color: #dc3545; border-color: #dc3545; } .btn-outline-danger:hover { color: #fff; background-color: #dc3545; border-color: #dc3545; } .btn-check:focus + .btn-outline-danger, .btn-outline-danger:focus { box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.5); } .btn-check:checked + .btn-outline-danger, .btn-check:active + .btn-outline-danger, .btn-outline-danger:active, .btn-outline-danger.active, .btn-outline-danger.dropdown-toggle.show { color: #fff; background-color: #dc3545; border-color: #dc3545; } .btn-check:checked + .btn-outline-danger:focus, .btn-check:active + .btn-outline-danger:focus, .btn-outline-danger:active:focus, .btn-outline-danger.active:focus, .btn-outline-danger.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.5); } .btn-outline-danger:disabled, .btn-outline-danger.disabled { color: #dc3545; background-color: transparent; } .btn-outline-light { color: #f8f9fa; border-color: #f8f9fa; } .btn-outline-light:hover { color: #000; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-check:focus + .btn-outline-light, .btn-outline-light:focus { box-shadow: 0 0 0 0.25rem rgba(248, 249, 250, 0.5); } .btn-check:checked + .btn-outline-light, .btn-check:active + .btn-outline-light, .btn-outline-light:active, .btn-outline-light.active, .btn-outline-light.dropdown-toggle.show { color: #000; background-color: #f8f9fa; border-color: #f8f9fa; } .btn-check:checked + .btn-outline-light:focus, .btn-check:active + .btn-outline-light:focus, .btn-outline-light:active:focus, .btn-outline-light.active:focus, .btn-outline-light.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(248, 249, 250, 0.5); } .btn-outline-light:disabled, .btn-outline-light.disabled { color: #f8f9fa; background-color: transparent; } .btn-outline-dark { color: #212529; border-color: #212529; } .btn-outline-dark:hover { color: #fff; background-color: #212529; border-color: #212529; } .btn-check:focus + .btn-outline-dark, .btn-outline-dark:focus { box-shadow: 0 0 0 0.25rem rgba(33, 37, 41, 0.5); } .btn-check:checked + .btn-outline-dark, .btn-check:active + .btn-outline-dark, .btn-outline-dark:active, .btn-outline-dark.active, .btn-outline-dark.dropdown-toggle.show { color: #fff; background-color: #212529; border-color: #212529; } .btn-check:checked + .btn-outline-dark:focus, .btn-check:active + .btn-outline-dark:focus, .btn-outline-dark:active:focus, .btn-outline-dark.active:focus, .btn-outline-dark.dropdown-toggle.show:focus { box-shadow: 0 0 0 0.25rem rgba(33, 37, 41, 0.5); } .btn-outline-dark:disabled, .btn-outline-dark.disabled { color: #212529; background-color: transparent; } .btn-link { font-weight: 400; color: #0d6efd; text-decoration: underline; } .btn-link:hover { color: #0a58ca; } .btn-link:disabled, .btn-link.disabled { color: #6c757d; } .btn-lg, .btn-group-lg > .btn { padding: 0.5rem 1rem; font-size: 1.25rem; border-radius: 0.3rem; } .btn-sm, .btn-group-sm > .btn { padding: 0.25rem 0.5rem; font-size: 0.875rem; border-radius: 0.2rem; } .fade { transition: opacity 0.15s linear; } @media (prefers-reduced-motion: reduce) { .fade { transition: none; } } .fade:not(.show) { opacity: 0; } .collapse:not(.show) { display: none; } .collapsing { height: 0; overflow: hidden; transition: height 0.35s ease; } @media (prefers-reduced-motion: reduce) { .collapsing { transition: none; } } .collapsing.collapse-horizontal { width: 0; height: auto; transition: width 0.35s ease; } @media (prefers-reduced-motion: reduce) { .collapsing.collapse-horizontal { transition: none; } } .dropup, .dropend, .dropdown, .dropstart { position: relative; } .dropdown-toggle { white-space: nowrap; } .dropdown-toggle::after { display: inline-block; margin-right: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid; border-left: 0.3em solid transparent; border-bottom: 0; border-right: 0.3em solid transparent; } .dropdown-toggle:empty::after { margin-right: 0; } .dropdown-menu { position: absolute; z-index: 1000; display: none; min-width: 10rem; padding: 0.5rem 0; margin: 0; font-size: 1rem; color: #212529; text-align: right; list-style: none; background-color: #fff; background-clip: padding-box; border: 1px solid rgba(0, 0, 0, 0.15); border-radius: 0.25rem; } .dropdown-menu[data-bs-popper] { top: 100%; right: 0; margin-top: 0.125rem; } .dropdown-menu-start { --bs-position: start; } .dropdown-menu-start[data-bs-popper] { left: auto; right: 0; } .dropdown-menu-end { --bs-position: end; } .dropdown-menu-end[data-bs-popper] { left: 0; right: auto; } @media (min-width: 576px) { .dropdown-menu-sm-start { --bs-position: start; } .dropdown-menu-sm-start[data-bs-popper] { left: auto; right: 0; } .dropdown-menu-sm-end { --bs-position: end; } .dropdown-menu-sm-end[data-bs-popper] { left: 0; right: auto; } } @media (min-width: 768px) { .dropdown-menu-md-start { --bs-position: start; } .dropdown-menu-md-start[data-bs-popper] { left: auto; right: 0; } .dropdown-menu-md-end { --bs-position: end; } .dropdown-menu-md-end[data-bs-popper] { left: 0; right: auto; } } @media (min-width: 992px) { .dropdown-menu-lg-start { --bs-position: start; } .dropdown-menu-lg-start[data-bs-popper] { left: auto; right: 0; } .dropdown-menu-lg-end { --bs-position: end; } .dropdown-menu-lg-end[data-bs-popper] { left: 0; right: auto; } } @media (min-width: 1200px) { .dropdown-menu-xl-start { --bs-position: start; } .dropdown-menu-xl-start[data-bs-popper] { left: auto; right: 0; } .dropdown-menu-xl-end { --bs-position: end; } .dropdown-menu-xl-end[data-bs-popper] { left: 0; right: auto; } } @media (min-width: 1400px) { .dropdown-menu-xxl-start { --bs-position: start; } .dropdown-menu-xxl-start[data-bs-popper] { left: auto; right: 0; } .dropdown-menu-xxl-end { --bs-position: end; } .dropdown-menu-xxl-end[data-bs-popper] { left: 0; right: auto; } } .dropup .dropdown-menu[data-bs-popper] { top: auto; bottom: 100%; margin-top: 0; margin-bottom: 0.125rem; } .dropup .dropdown-toggle::after { display: inline-block; margin-right: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0; border-left: 0.3em solid transparent; border-bottom: 0.3em solid; border-right: 0.3em solid transparent; } .dropup .dropdown-toggle:empty::after { margin-right: 0; } .dropend .dropdown-menu[data-bs-popper] { top: 0; left: auto; right: 100%; margin-top: 0; margin-right: 0.125rem; } .dropend .dropdown-toggle::after { display: inline-block; margin-right: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid transparent; border-left: 0; border-bottom: 0.3em solid transparent; border-right: 0.3em solid; } .dropend .dropdown-toggle:empty::after { margin-right: 0; } .dropend .dropdown-toggle::after { vertical-align: 0; } .dropstart .dropdown-menu[data-bs-popper] { top: 0; left: 100%; right: auto; margin-top: 0; margin-left: 0.125rem; } .dropstart .dropdown-toggle::after { display: inline-block; margin-right: 0.255em; vertical-align: 0.255em; content: ""; } .dropstart .dropdown-toggle::after { display: none; } .dropstart .dropdown-toggle::before { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid transparent; border-left: 0.3em solid; border-bottom: 0.3em solid transparent; } .dropstart .dropdown-toggle:empty::after { margin-right: 0; } .dropstart .dropdown-toggle::before { vertical-align: 0; } .dropdown-divider { height: 0; margin: 0.5rem 0; overflow: hidden; border-top: 1px solid rgba(0, 0, 0, 0.15); } .dropdown-item { display: block; width: 100%; padding: 0.25rem 1rem; clear: both; font-weight: 400; color: #212529; text-align: inherit; text-decoration: none; white-space: nowrap; background-color: transparent; border: 0; } .dropdown-item:hover, .dropdown-item:focus { color: #1e2125; background-color: #e9ecef; } .dropdown-item.active, .dropdown-item:active { color: #fff; text-decoration: none; background-color: #0d6efd; } .dropdown-item.disabled, .dropdown-item:disabled { color: #adb5bd; pointer-events: none; background-color: transparent; } .dropdown-menu.show { display: block; } .dropdown-header { display: block; padding: 0.5rem 1rem; margin-bottom: 0; font-size: 0.875rem; color: #6c757d; white-space: nowrap; } .dropdown-item-text { display: block; padding: 0.25rem 1rem; color: #212529; } .dropdown-menu-dark { color: #dee2e6; background-color: #343a40; border-color: rgba(0, 0, 0, 0.15); } .dropdown-menu-dark .dropdown-item { color: #dee2e6; } .dropdown-menu-dark .dropdown-item:hover, .dropdown-menu-dark .dropdown-item:focus { color: #fff; background-color: rgba(255, 255, 255, 0.15); } .dropdown-menu-dark .dropdown-item.active, .dropdown-menu-dark .dropdown-item:active { color: #fff; background-color: #0d6efd; } .dropdown-menu-dark .dropdown-item.disabled, .dropdown-menu-dark .dropdown-item:disabled { color: #adb5bd; } .dropdown-menu-dark .dropdown-divider { border-color: rgba(0, 0, 0, 0.15); } .dropdown-menu-dark .dropdown-item-text { color: #dee2e6; } .dropdown-menu-dark .dropdown-header { color: #adb5bd; } .btn-group, .btn-group-vertical { position: relative; display: inline-flex; vertical-align: middle; } .btn-group > .btn, .btn-group-vertical > .btn { position: relative; flex: 1 1 auto; } .btn-group > .btn-check:checked + .btn, .btn-group > .btn-check:focus + .btn, .btn-group > .btn:hover, .btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, .btn-group-vertical > .btn-check:checked + .btn, .btn-group-vertical > .btn-check:focus + .btn, .btn-group-vertical > .btn:hover, .btn-group-vertical > .btn:focus, .btn-group-vertical > .btn:active, .btn-group-vertical > .btn.active { z-index: 1; } .btn-toolbar { display: flex; flex-wrap: wrap; justify-content: flex-start; } .btn-toolbar .input-group { width: auto; } .btn-group > .btn:not(:first-child), .btn-group > .btn-group:not(:first-child) { margin-right: -1px; } .btn-group > .btn:not(:last-child):not(.dropdown-toggle), .btn-group > .btn-group:not(:last-child) > .btn { border-top-left-radius: 0; border-bottom-left-radius: 0; } .btn-group > .btn:nth-child(n + 3), .btn-group > :not(.btn-check) + .btn, .btn-group > .btn-group:not(:first-child) > .btn { border-top-right-radius: 0; border-bottom-right-radius: 0; } .dropdown-toggle-split { padding-left: 0.5625rem; padding-right: 0.5625rem; } .dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after { margin-right: 0; } .dropstart .dropdown-toggle-split::before { margin-left: 0; } .btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { padding-left: 0.375rem; padding-right: 0.375rem; } .btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { padding-left: 0.75rem; padding-right: 0.75rem; } .btn-group-vertical { flex-direction: column; align-items: flex-start; justify-content: center; } .btn-group-vertical > .btn, .btn-group-vertical > .btn-group { width: 100%; } .btn-group-vertical > .btn:not(:first-child), .btn-group-vertical > .btn-group:not(:first-child) { margin-top: -1px; } .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), .btn-group-vertical > .btn-group:not(:last-child) > .btn { border-bottom-left-radius: 0; border-bottom-right-radius: 0; } .btn-group-vertical > .btn ~ .btn, .btn-group-vertical > .btn-group:not(:first-child) > .btn { border-top-right-radius: 0; border-top-left-radius: 0; } .nav { display: flex; flex-wrap: wrap; padding-right: 0; margin-bottom: 0; list-style: none; } .nav-link { display: block; padding: 0.5rem 1rem; color: #0d6efd; text-decoration: none; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .nav-link { transition: none; } } .nav-link:hover, .nav-link:focus { color: #0a58ca; } .nav-link.disabled { color: #6c757d; pointer-events: none; cursor: default; } .nav-tabs { border-bottom: 1px solid #dee2e6; } .nav-tabs .nav-link { margin-bottom: -1px; background: none; border: 1px solid transparent; border-top-right-radius: 0.25rem; border-top-left-radius: 0.25rem; } .nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { border-color: #e9ecef #e9ecef #dee2e6; isolation: isolate; } .nav-tabs .nav-link.disabled { color: #6c757d; background-color: transparent; border-color: transparent; } .nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link { color: #495057; background-color: #fff; border-color: #dee2e6 #dee2e6 #fff; } .nav-tabs .dropdown-menu { margin-top: -1px; border-top-right-radius: 0; border-top-left-radius: 0; } .nav-pills .nav-link { background: none; border: 0; border-radius: 0.25rem; } .nav-pills .nav-link.active, .nav-pills .show > .nav-link { color: #fff; background-color: #0d6efd; } .nav-fill > .nav-link, .nav-fill .nav-item { flex: 1 1 auto; text-align: center; } .nav-justified > .nav-link, .nav-justified .nav-item { flex-basis: 0; flex-grow: 1; text-align: center; } .nav-fill .nav-item .nav-link, .nav-justified .nav-item .nav-link { width: 100%; } .tab-content > .tab-pane { display: none; } .tab-content > .active { display: block; } .navbar { position: relative; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; padding-top: 0.5rem; padding-bottom: 0.5rem; } .navbar > .container, .navbar > .container-fluid, .navbar > .container-sm, .navbar > .container-md, .navbar > .container-lg, .navbar > .container-xl, .navbar > .container-xxl { display: flex; flex-wrap: inherit; align-items: center; justify-content: space-between; } .navbar-brand { padding-top: 0.3125rem; padding-bottom: 0.3125rem; margin-left: 1rem; font-size: 1.25rem; text-decoration: none; white-space: nowrap; } .navbar-nav { display: flex; flex-direction: column; padding-right: 0; margin-bottom: 0; list-style: none; } .navbar-nav .nav-link { padding-left: 0; padding-right: 0; } .navbar-nav .dropdown-menu { position: static; } .navbar-text { padding-top: 0.5rem; padding-bottom: 0.5rem; } .navbar-collapse { flex-basis: 100%; flex-grow: 1; align-items: center; } .navbar-toggler { padding: 0.25rem 0.75rem; font-size: 1.25rem; line-height: 1; background-color: transparent; border: 1px solid transparent; border-radius: 0.25rem; transition: box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .navbar-toggler { transition: none; } } .navbar-toggler:hover { text-decoration: none; } .navbar-toggler:focus { text-decoration: none; outline: 0; box-shadow: 0 0 0 0.25rem; } .navbar-toggler-icon { display: inline-block; width: 1.5em; height: 1.5em; vertical-align: middle; background-repeat: no-repeat; background-position: center; background-size: 100%; } .navbar-nav-scroll { max-height: var(--bs-scroll-height, 75vh); overflow-y: auto; } @media (min-width: 576px) { .navbar-expand-sm { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-sm .navbar-nav { flex-direction: row; } .navbar-expand-sm .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-sm .navbar-nav .nav-link { padding-left: 0.5rem; padding-right: 0.5rem; } .navbar-expand-sm .navbar-nav-scroll { overflow: visible; } .navbar-expand-sm .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-sm .navbar-toggler { display: none; } .navbar-expand-sm .offcanvas-header { display: none; } .navbar-expand-sm .offcanvas { position: inherit; bottom: 0; z-index: 1000; flex-grow: 1; visibility: visible !important; background-color: transparent; border-left: 0; border-right: 0; transition: none; transform: none; } .navbar-expand-sm .offcanvas-top, .navbar-expand-sm .offcanvas-bottom { height: auto; border-top: 0; border-bottom: 0; } .navbar-expand-sm .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } @media (min-width: 768px) { .navbar-expand-md { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-md .navbar-nav { flex-direction: row; } .navbar-expand-md .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-md .navbar-nav .nav-link { padding-left: 0.5rem; padding-right: 0.5rem; } .navbar-expand-md .navbar-nav-scroll { overflow: visible; } .navbar-expand-md .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-md .navbar-toggler { display: none; } .navbar-expand-md .offcanvas-header { display: none; } .navbar-expand-md .offcanvas { position: inherit; bottom: 0; z-index: 1000; flex-grow: 1; visibility: visible !important; background-color: transparent; border-left: 0; border-right: 0; transition: none; transform: none; } .navbar-expand-md .offcanvas-top, .navbar-expand-md .offcanvas-bottom { height: auto; border-top: 0; border-bottom: 0; } .navbar-expand-md .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } @media (min-width: 992px) { .navbar-expand-lg { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-lg .navbar-nav { flex-direction: row; } .navbar-expand-lg .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-lg .navbar-nav .nav-link { padding-left: 0.5rem; padding-right: 0.5rem; } .navbar-expand-lg .navbar-nav-scroll { overflow: visible; } .navbar-expand-lg .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-lg .navbar-toggler { display: none; } .navbar-expand-lg .offcanvas-header { display: none; } .navbar-expand-lg .offcanvas { position: inherit; bottom: 0; z-index: 1000; flex-grow: 1; visibility: visible !important; background-color: transparent; border-left: 0; border-right: 0; transition: none; transform: none; } .navbar-expand-lg .offcanvas-top, .navbar-expand-lg .offcanvas-bottom { height: auto; border-top: 0; border-bottom: 0; } .navbar-expand-lg .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } @media (min-width: 1200px) { .navbar-expand-xl { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-xl .navbar-nav { flex-direction: row; } .navbar-expand-xl .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-xl .navbar-nav .nav-link { padding-left: 0.5rem; padding-right: 0.5rem; } .navbar-expand-xl .navbar-nav-scroll { overflow: visible; } .navbar-expand-xl .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-xl .navbar-toggler { display: none; } .navbar-expand-xl .offcanvas-header { display: none; } .navbar-expand-xl .offcanvas { position: inherit; bottom: 0; z-index: 1000; flex-grow: 1; visibility: visible !important; background-color: transparent; border-left: 0; border-right: 0; transition: none; transform: none; } .navbar-expand-xl .offcanvas-top, .navbar-expand-xl .offcanvas-bottom { height: auto; border-top: 0; border-bottom: 0; } .navbar-expand-xl .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } @media (min-width: 1400px) { .navbar-expand-xxl { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-xxl .navbar-nav { flex-direction: row; } .navbar-expand-xxl .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-xxl .navbar-nav .nav-link { padding-left: 0.5rem; padding-right: 0.5rem; } .navbar-expand-xxl .navbar-nav-scroll { overflow: visible; } .navbar-expand-xxl .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-xxl .navbar-toggler { display: none; } .navbar-expand-xxl .offcanvas-header { display: none; } .navbar-expand-xxl .offcanvas { position: inherit; bottom: 0; z-index: 1000; flex-grow: 1; visibility: visible !important; background-color: transparent; border-left: 0; border-right: 0; transition: none; transform: none; } .navbar-expand-xxl .offcanvas-top, .navbar-expand-xxl .offcanvas-bottom { height: auto; border-top: 0; border-bottom: 0; } .navbar-expand-xxl .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } .navbar-expand { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand .navbar-nav { flex-direction: row; } .navbar-expand .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand .navbar-nav .nav-link { padding-left: 0.5rem; padding-right: 0.5rem; } .navbar-expand .navbar-nav-scroll { overflow: visible; } .navbar-expand .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand .navbar-toggler { display: none; } .navbar-expand .offcanvas-header { display: none; } .navbar-expand .offcanvas { position: inherit; bottom: 0; z-index: 1000; flex-grow: 1; visibility: visible !important; background-color: transparent; border-left: 0; border-right: 0; transition: none; transform: none; } .navbar-expand .offcanvas-top, .navbar-expand .offcanvas-bottom { height: auto; border-top: 0; border-bottom: 0; } .navbar-expand .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } .navbar-light .navbar-brand { color: rgba(0, 0, 0, 0.9); } .navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { color: rgba(0, 0, 0, 0.9); } .navbar-light .navbar-nav .nav-link { color: rgba(0, 0, 0, 0.55); } .navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { color: rgba(0, 0, 0, 0.7); } .navbar-light .navbar-nav .nav-link.disabled { color: rgba(0, 0, 0, 0.3); } .navbar-light .navbar-nav .show > .nav-link, .navbar-light .navbar-nav .nav-link.active { color: rgba(0, 0, 0, 0.9); } .navbar-light .navbar-toggler { color: rgba(0, 0, 0, 0.55); border-color: rgba(0, 0, 0, 0.1); } .navbar-light .navbar-toggler-icon { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .navbar-light .navbar-text { color: rgba(0, 0, 0, 0.55); } .navbar-light .navbar-text a, .navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { color: rgba(0, 0, 0, 0.9); } .navbar-dark .navbar-brand { color: #fff; } .navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { color: #fff; } .navbar-dark .navbar-nav .nav-link { color: rgba(255, 255, 255, 0.55); } .navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { color: rgba(255, 255, 255, 0.75); } .navbar-dark .navbar-nav .nav-link.disabled { color: rgba(255, 255, 255, 0.25); } .navbar-dark .navbar-nav .show > .nav-link, .navbar-dark .navbar-nav .nav-link.active { color: #fff; } .navbar-dark .navbar-toggler { color: rgba(255, 255, 255, 0.55); border-color: rgba(255, 255, 255, 0.1); } .navbar-dark .navbar-toggler-icon { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .navbar-dark .navbar-text { color: rgba(255, 255, 255, 0.55); } .navbar-dark .navbar-text a, .navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { color: #fff; } .card { position: relative; display: flex; flex-direction: column; min-width: 0; word-wrap: break-word; background-color: #fff; background-clip: border-box; border: 1px solid rgba(0, 0, 0, 0.125); border-radius: 0.25rem; } .card > hr { margin-left: 0; margin-right: 0; } .card > .list-group { border-top: inherit; border-bottom: inherit; } .card > .list-group:first-child { border-top-width: 0; border-top-right-radius: calc(0.25rem - 1px); border-top-left-radius: calc(0.25rem - 1px); } .card > .list-group:last-child { border-bottom-width: 0; border-bottom-left-radius: calc(0.25rem - 1px); border-bottom-right-radius: calc(0.25rem - 1px); } .card > .card-header + .list-group, .card > .list-group + .card-footer { border-top: 0; } .card-body { flex: 1 1 auto; padding: 1rem 1rem; } .card-title { margin-bottom: 0.5rem; } .card-subtitle { margin-top: -0.25rem; margin-bottom: 0; } .card-text:last-child { margin-bottom: 0; } .card-link + .card-link { margin-right: 1rem; } .card-header { padding: 0.5rem 1rem; margin-bottom: 0; background-color: rgba(0, 0, 0, 0.03); border-bottom: 1px solid rgba(0, 0, 0, 0.125); } .card-header:first-child { border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; } .card-footer { padding: 0.5rem 1rem; background-color: rgba(0, 0, 0, 0.03); border-top: 1px solid rgba(0, 0, 0, 0.125); } .card-footer:last-child { border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); } .card-header-tabs { margin-left: -0.5rem; margin-bottom: -0.5rem; margin-right: -0.5rem; border-bottom: 0; } .card-header-pills { margin-left: -0.5rem; margin-right: -0.5rem; } .card-img-overlay { position: absolute; top: 0; left: 0; bottom: 0; right: 0; padding: 1rem; border-radius: calc(0.25rem - 1px); } .card-img, .card-img-top, .card-img-bottom { width: 100%; } .card-img, .card-img-top { border-top-right-radius: calc(0.25rem - 1px); border-top-left-radius: calc(0.25rem - 1px); } .card-img, .card-img-bottom { border-bottom-left-radius: calc(0.25rem - 1px); border-bottom-right-radius: calc(0.25rem - 1px); } .card-group > .card { margin-bottom: 0.75rem; } @media (min-width: 576px) { .card-group { display: flex; flex-flow: row wrap; } .card-group > .card { flex: 1 0 0%; margin-bottom: 0; } .card-group > .card + .card { margin-right: 0; border-right: 0; } .card-group > .card:not(:last-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; } .card-group > .card:not(:last-child) .card-img-top, .card-group > .card:not(:last-child) .card-header { border-top-left-radius: 0; } .card-group > .card:not(:last-child) .card-img-bottom, .card-group > .card:not(:last-child) .card-footer { border-bottom-left-radius: 0; } .card-group > .card:not(:first-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .card-group > .card:not(:first-child) .card-img-top, .card-group > .card:not(:first-child) .card-header { border-top-right-radius: 0; } .card-group > .card:not(:first-child) .card-img-bottom, .card-group > .card:not(:first-child) .card-footer { border-bottom-right-radius: 0; } } .accordion-button { position: relative; display: flex; align-items: center; width: 100%; padding: 1rem 1.25rem; font-size: 1rem; color: #212529; text-align: right; background-color: #fff; border: 0; border-radius: 0; overflow-anchor: none; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease; } @media (prefers-reduced-motion: reduce) { .accordion-button { transition: none; } } .accordion-button:not(.collapsed) { color: #0c63e4; background-color: #e7f1ff; box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.125); } .accordion-button:not(.collapsed)::after { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); transform: rotate(180deg); } .accordion-button::after { flex-shrink: 0; width: 1.25rem; height: 1.25rem; margin-right: auto; content: ""; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-size: 1.25rem; transition: transform 0.2s ease-in-out; } @media (prefers-reduced-motion: reduce) { .accordion-button::after { transition: none; } } .accordion-button:hover { z-index: 2; } .accordion-button:focus { z-index: 3; border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .accordion-header { margin-bottom: 0; } .accordion-item { background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.125); } .accordion-item:first-of-type { border-top-right-radius: 0.25rem; border-top-left-radius: 0.25rem; } .accordion-item:first-of-type .accordion-button { border-top-right-radius: calc(0.25rem - 1px); border-top-left-radius: calc(0.25rem - 1px); } .accordion-item:not(:first-of-type) { border-top: 0; } .accordion-item:last-of-type { border-bottom-left-radius: 0.25rem; border-bottom-right-radius: 0.25rem; } .accordion-item:last-of-type .accordion-button.collapsed { border-bottom-left-radius: calc(0.25rem - 1px); border-bottom-right-radius: calc(0.25rem - 1px); } .accordion-item:last-of-type .accordion-collapse { border-bottom-left-radius: 0.25rem; border-bottom-right-radius: 0.25rem; } .accordion-body { padding: 1rem 1.25rem; } .accordion-flush .accordion-collapse { border-width: 0; } .accordion-flush .accordion-item { border-left: 0; border-right: 0; border-radius: 0; } .accordion-flush .accordion-item:first-child { border-top: 0; } .accordion-flush .accordion-item:last-child { border-bottom: 0; } .accordion-flush .accordion-item .accordion-button { border-radius: 0; } .breadcrumb { display: flex; flex-wrap: wrap; padding: 0 0; margin-bottom: 1rem; list-style: none; } .breadcrumb-item + .breadcrumb-item { padding-right: 0.5rem; } .breadcrumb-item + .breadcrumb-item::before { float: right; padding-left: 0.5rem; color: #6c757d; content: var(--bs-breadcrumb-divider, "/"); } .breadcrumb-item.active { color: #6c757d; } .pagination { display: flex; padding-right: 0; list-style: none; } .page-link { position: relative; display: block; color: #0d6efd; text-decoration: none; background-color: #fff; border: 1px solid #dee2e6; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .page-link { transition: none; } } .page-link:hover { z-index: 2; color: #0a58ca; background-color: #e9ecef; border-color: #dee2e6; } .page-link:focus { z-index: 3; color: #0a58ca; background-color: #e9ecef; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .page-item:not(:first-child) .page-link { margin-right: -1px; } .page-item.active .page-link { z-index: 3; color: #fff; background-color: #0d6efd; border-color: #0d6efd; } .page-item.disabled .page-link { color: #6c757d; pointer-events: none; background-color: #fff; border-color: #dee2e6; } .page-link { padding: 0.375rem 0.75rem; } .page-item:first-child .page-link { border-top-right-radius: 0.25rem; border-bottom-right-radius: 0.25rem; } .page-item:last-child .page-link { border-top-left-radius: 0.25rem; border-bottom-left-radius: 0.25rem; } .pagination-lg .page-link { padding: 0.75rem 1.5rem; font-size: 1.25rem; } .pagination-lg .page-item:first-child .page-link { border-top-right-radius: 0.3rem; border-bottom-right-radius: 0.3rem; } .pagination-lg .page-item:last-child .page-link { border-top-left-radius: 0.3rem; border-bottom-left-radius: 0.3rem; } .pagination-sm .page-link { padding: 0.25rem 0.5rem; font-size: 0.875rem; } .pagination-sm .page-item:first-child .page-link { border-top-right-radius: 0.2rem; border-bottom-right-radius: 0.2rem; } .pagination-sm .page-item:last-child .page-link { border-top-left-radius: 0.2rem; border-bottom-left-radius: 0.2rem; } .badge { display: inline-block; padding: 0.35em 0.65em; font-size: 0.75em; font-weight: 700; line-height: 1; color: #fff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: 0.25rem; } .badge:empty { display: none; } .btn .badge { position: relative; top: -1px; } .alert { position: relative; padding: 1rem 1rem; margin-bottom: 1rem; border: 1px solid transparent; border-radius: 0.25rem; } .alert-heading { color: inherit; } .alert-link { font-weight: 700; } .alert-dismissible { padding-left: 3rem; } .alert-dismissible .btn-close { position: absolute; top: 0; left: 0; z-index: 2; padding: 1.25rem 1rem; } .alert-primary { color: #084298; background-color: #cfe2ff; border-color: #b6d4fe; } .alert-primary .alert-link { color: #06357a; } .alert-secondary { color: #41464b; background-color: #e2e3e5; border-color: #d3d6d8; } .alert-secondary .alert-link { color: #34383c; } .alert-success { color: #0f5132; background-color: #d1e7dd; border-color: #badbcc; } .alert-success .alert-link { color: #0c4128; } .alert-info { color: #055160; background-color: #cff4fc; border-color: #b6effb; } .alert-info .alert-link { color: #04414d; } .alert-warning { color: #664d03; background-color: #fff3cd; border-color: #ffecb5; } .alert-warning .alert-link { color: #523e02; } .alert-danger { color: #842029; background-color: #f8d7da; border-color: #f5c2c7; } .alert-danger .alert-link { color: #6a1a21; } .alert-light { color: #636464; background-color: #fefefe; border-color: #fdfdfe; } .alert-light .alert-link { color: #4f5050; } .alert-dark { color: #141619; background-color: #d3d3d4; border-color: #bcbebf; } .alert-dark .alert-link { color: #101214; } @-webkit-keyframes progress-bar-stripes { 0% { background-position-x: 1rem; } } @keyframes progress-bar-stripes { 0% { background-position-x: 1rem; } } .progress { display: flex; height: 1rem; overflow: hidden; font-size: 0.75rem; background-color: #e9ecef; border-radius: 0.25rem; } .progress-bar { display: flex; flex-direction: column; justify-content: center; overflow: hidden; color: #fff; text-align: center; white-space: nowrap; background-color: #0d6efd; transition: width 0.6s ease; } @media (prefers-reduced-motion: reduce) { .progress-bar { transition: none; } } .progress-bar-striped { background-image: linear-gradient( -45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent ); background-size: 1rem 1rem; } .progress-bar-animated { -webkit-animation: 1s linear infinite progress-bar-stripes; animation: 1s linear infinite progress-bar-stripes; } @media (prefers-reduced-motion: reduce) { .progress-bar-animated { -webkit-animation: none; animation: none; } } .list-group { display: flex; flex-direction: column; padding-right: 0; margin-bottom: 0; border-radius: 0.25rem; } .list-group-numbered { list-style-type: none; counter-reset: section; } .list-group-numbered > li::before { content: counters(section, ".") ". "; counter-increment: section; } .list-group-item-action { width: 100%; color: #495057; text-align: inherit; } .list-group-item-action:hover, .list-group-item-action:focus { z-index: 1; color: #495057; text-decoration: none; background-color: #f8f9fa; } .list-group-item-action:active { color: #212529; background-color: #e9ecef; } .list-group-item { position: relative; display: block; padding: 0.5rem 1rem; color: #212529; text-decoration: none; background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.125); } .list-group-item:first-child { border-top-right-radius: inherit; border-top-left-radius: inherit; } .list-group-item:last-child { border-bottom-left-radius: inherit; border-bottom-right-radius: inherit; } .list-group-item.disabled, .list-group-item:disabled { color: #6c757d; pointer-events: none; background-color: #fff; } .list-group-item.active { z-index: 2; color: #fff; background-color: #0d6efd; border-color: #0d6efd; } .list-group-item + .list-group-item { border-top-width: 0; } .list-group-item + .list-group-item.active { margin-top: -1px; border-top-width: 1px; } .list-group-horizontal { flex-direction: row; } .list-group-horizontal > .list-group-item:first-child { border-bottom-right-radius: 0.25rem; border-top-left-radius: 0; } .list-group-horizontal > .list-group-item:last-child { border-top-left-radius: 0.25rem; border-bottom-right-radius: 0; } .list-group-horizontal > .list-group-item.active { margin-top: 0; } .list-group-horizontal > .list-group-item + .list-group-item { border-top-width: 1px; border-right-width: 0; } .list-group-horizontal > .list-group-item + .list-group-item.active { margin-right: -1px; border-right-width: 1px; } @media (min-width: 576px) { .list-group-horizontal-sm { flex-direction: row; } .list-group-horizontal-sm > .list-group-item:first-child { border-bottom-right-radius: 0.25rem; border-top-left-radius: 0; } .list-group-horizontal-sm > .list-group-item:last-child { border-top-left-radius: 0.25rem; border-bottom-right-radius: 0; } .list-group-horizontal-sm > .list-group-item.active { margin-top: 0; } .list-group-horizontal-sm > .list-group-item + .list-group-item { border-top-width: 1px; border-right-width: 0; } .list-group-horizontal-sm > .list-group-item + .list-group-item.active { margin-right: -1px; border-right-width: 1px; } } @media (min-width: 768px) { .list-group-horizontal-md { flex-direction: row; } .list-group-horizontal-md > .list-group-item:first-child { border-bottom-right-radius: 0.25rem; border-top-left-radius: 0; } .list-group-horizontal-md > .list-group-item:last-child { border-top-left-radius: 0.25rem; border-bottom-right-radius: 0; } .list-group-horizontal-md > .list-group-item.active { margin-top: 0; } .list-group-horizontal-md > .list-group-item + .list-group-item { border-top-width: 1px; border-right-width: 0; } .list-group-horizontal-md > .list-group-item + .list-group-item.active { margin-right: -1px; border-right-width: 1px; } } @media (min-width: 992px) { .list-group-horizontal-lg { flex-direction: row; } .list-group-horizontal-lg > .list-group-item:first-child { border-bottom-right-radius: 0.25rem; border-top-left-radius: 0; } .list-group-horizontal-lg > .list-group-item:last-child { border-top-left-radius: 0.25rem; border-bottom-right-radius: 0; } .list-group-horizontal-lg > .list-group-item.active { margin-top: 0; } .list-group-horizontal-lg > .list-group-item + .list-group-item { border-top-width: 1px; border-right-width: 0; } .list-group-horizontal-lg > .list-group-item + .list-group-item.active { margin-right: -1px; border-right-width: 1px; } } @media (min-width: 1200px) { .list-group-horizontal-xl { flex-direction: row; } .list-group-horizontal-xl > .list-group-item:first-child { border-bottom-right-radius: 0.25rem; border-top-left-radius: 0; } .list-group-horizontal-xl > .list-group-item:last-child { border-top-left-radius: 0.25rem; border-bottom-right-radius: 0; } .list-group-horizontal-xl > .list-group-item.active { margin-top: 0; } .list-group-horizontal-xl > .list-group-item + .list-group-item { border-top-width: 1px; border-right-width: 0; } .list-group-horizontal-xl > .list-group-item + .list-group-item.active { margin-right: -1px; border-right-width: 1px; } } @media (min-width: 1400px) { .list-group-horizontal-xxl { flex-direction: row; } .list-group-horizontal-xxl > .list-group-item:first-child { border-bottom-right-radius: 0.25rem; border-top-left-radius: 0; } .list-group-horizontal-xxl > .list-group-item:last-child { border-top-left-radius: 0.25rem; border-bottom-right-radius: 0; } .list-group-horizontal-xxl > .list-group-item.active { margin-top: 0; } .list-group-horizontal-xxl > .list-group-item + .list-group-item { border-top-width: 1px; border-right-width: 0; } .list-group-horizontal-xxl > .list-group-item + .list-group-item.active { margin-right: -1px; border-right-width: 1px; } } .list-group-flush { border-radius: 0; } .list-group-flush > .list-group-item { border-width: 0 0 1px; } .list-group-flush > .list-group-item:last-child { border-bottom-width: 0; } .list-group-item-primary { color: #084298; background-color: #cfe2ff; } .list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { color: #084298; background-color: #bacbe6; } .list-group-item-primary.list-group-item-action.active { color: #fff; background-color: #084298; border-color: #084298; } .list-group-item-secondary { color: #41464b; background-color: #e2e3e5; } .list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { color: #41464b; background-color: #cbccce; } .list-group-item-secondary.list-group-item-action.active { color: #fff; background-color: #41464b; border-color: #41464b; } .list-group-item-success { color: #0f5132; background-color: #d1e7dd; } .list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { color: #0f5132; background-color: #bcd0c7; } .list-group-item-success.list-group-item-action.active { color: #fff; background-color: #0f5132; border-color: #0f5132; } .list-group-item-info { color: #055160; background-color: #cff4fc; } .list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { color: #055160; background-color: #badce3; } .list-group-item-info.list-group-item-action.active { color: #fff; background-color: #055160; border-color: #055160; } .list-group-item-warning { color: #664d03; background-color: #fff3cd; } .list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { color: #664d03; background-color: #e6dbb9; } .list-group-item-warning.list-group-item-action.active { color: #fff; background-color: #664d03; border-color: #664d03; } .list-group-item-danger { color: #842029; background-color: #f8d7da; } .list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { color: #842029; background-color: #dfc2c4; } .list-group-item-danger.list-group-item-action.active { color: #fff; background-color: #842029; border-color: #842029; } .list-group-item-light { color: #636464; background-color: #fefefe; } .list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { color: #636464; background-color: #e5e5e5; } .list-group-item-light.list-group-item-action.active { color: #fff; background-color: #636464; border-color: #636464; } .list-group-item-dark { color: #141619; background-color: #d3d3d4; } .list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { color: #141619; background-color: #bebebf; } .list-group-item-dark.list-group-item-action.active { color: #fff; background-color: #141619; border-color: #141619; } .btn-close { box-sizing: content-box; width: 1em; height: 1em; padding: 0.25em 0.25em; color: #000; background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat; border: 0; border-radius: 0.25rem; opacity: 0.5; } .btn-close:hover { color: #000; text-decoration: none; opacity: 0.75; } .btn-close:focus { outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); opacity: 1; } .btn-close:disabled, .btn-close.disabled { pointer-events: none; -webkit-user-select: none; -moz-user-select: none; user-select: none; opacity: 0.25; } .btn-close-white { filter: invert(1) grayscale(100%) brightness(200%); } .toast { width: 350px; max-width: 100%; font-size: 0.875rem; pointer-events: auto; background-color: rgba(255, 255, 255, 0.85); background-clip: padding-box; border: 1px solid rgba(0, 0, 0, 0.1); box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); border-radius: 0.25rem; } .toast.showing { opacity: 0; } .toast:not(.show) { display: none; } .toast-container { width: -webkit-max-content; width: -moz-max-content; width: max-content; max-width: 100%; pointer-events: none; } .toast-container > :not(:last-child) { margin-bottom: 0.75rem; } .toast-header { display: flex; align-items: center; padding: 0.5rem 0.75rem; color: #6c757d; background-color: rgba(255, 255, 255, 0.85); background-clip: padding-box; border-bottom: 1px solid rgba(0, 0, 0, 0.05); border-top-right-radius: calc(0.25rem - 1px); border-top-left-radius: calc(0.25rem - 1px); } .toast-header .btn-close { margin-left: -0.375rem; margin-right: 0.75rem; } .toast-body { padding: 0.75rem; word-wrap: break-word; } .modal { position: fixed; top: 0; right: 0; z-index: 1055; display: none; width: 100%; height: 100%; overflow-x: hidden; overflow-y: auto; outline: 0; } .modal-dialog { position: relative; width: auto; margin: 0.5rem; pointer-events: none; } .modal.fade .modal-dialog { transition: transform 0.3s ease-out; transform: translate(0, -50px); } @media (prefers-reduced-motion: reduce) { .modal.fade .modal-dialog { transition: none; } } .modal.show .modal-dialog { transform: none; } .modal.modal-static .modal-dialog { transform: scale(1.02); } .modal-dialog-scrollable { height: calc(100% - 1rem); } .modal-dialog-scrollable .modal-content { max-height: 100%; overflow: hidden; } .modal-dialog-scrollable .modal-body { overflow-y: auto; } .modal-dialog-centered { display: flex; align-items: center; min-height: calc(100% - 1rem); } .modal-content { position: relative; display: flex; flex-direction: column; width: 100%; pointer-events: auto; background-color: #fff; background-clip: padding-box; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 0.3rem; outline: 0; } .modal-backdrop { position: fixed; top: 0; right: 0; z-index: 1050; width: 100vw; height: 100vh; background-color: #000; } .modal-backdrop.fade { opacity: 0; } .modal-backdrop.show { opacity: 0.5; } .modal-header { display: flex; flex-shrink: 0; align-items: center; justify-content: space-between; padding: 1rem 1rem; border-bottom: 1px solid #dee2e6; border-top-right-radius: calc(0.3rem - 1px); border-top-left-radius: calc(0.3rem - 1px); } .modal-header .btn-close { padding: 0.5rem 0.5rem; margin: -0.5rem auto -0.5rem -0.5rem; } .modal-title { margin-bottom: 0; line-height: 1.5; } .modal-body { position: relative; flex: 1 1 auto; padding: 1rem; } .modal-footer { display: flex; flex-wrap: wrap; flex-shrink: 0; align-items: center; justify-content: flex-end; padding: 0.75rem; border-top: 1px solid #dee2e6; border-bottom-left-radius: calc(0.3rem - 1px); border-bottom-right-radius: calc(0.3rem - 1px); } .modal-footer > * { margin: 0.25rem; } @media (min-width: 576px) { .modal-dialog { max-width: 500px; margin: 1.75rem auto; } .modal-dialog-scrollable { height: calc(100% - 3.5rem); } .modal-dialog-centered { min-height: calc(100% - 3.5rem); } .modal-sm { max-width: 300px; } } @media (min-width: 992px) { .modal-lg, .modal-xl { max-width: 800px; } } @media (min-width: 1200px) { .modal-xl { max-width: 1140px; } } .modal-fullscreen { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen .modal-header { border-radius: 0; } .modal-fullscreen .modal-body { overflow-y: auto; } .modal-fullscreen .modal-footer { border-radius: 0; } @media (max-width: 575.98px) { .modal-fullscreen-sm-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-sm-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-sm-down .modal-header { border-radius: 0; } .modal-fullscreen-sm-down .modal-body { overflow-y: auto; } .modal-fullscreen-sm-down .modal-footer { border-radius: 0; } } @media (max-width: 767.98px) { .modal-fullscreen-md-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-md-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-md-down .modal-header { border-radius: 0; } .modal-fullscreen-md-down .modal-body { overflow-y: auto; } .modal-fullscreen-md-down .modal-footer { border-radius: 0; } } @media (max-width: 991.98px) { .modal-fullscreen-lg-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-lg-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-lg-down .modal-header { border-radius: 0; } .modal-fullscreen-lg-down .modal-body { overflow-y: auto; } .modal-fullscreen-lg-down .modal-footer { border-radius: 0; } } @media (max-width: 1199.98px) { .modal-fullscreen-xl-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-xl-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-xl-down .modal-header { border-radius: 0; } .modal-fullscreen-xl-down .modal-body { overflow-y: auto; } .modal-fullscreen-xl-down .modal-footer { border-radius: 0; } } @media (max-width: 1399.98px) { .modal-fullscreen-xxl-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-xxl-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-xxl-down .modal-header { border-radius: 0; } .modal-fullscreen-xxl-down .modal-body { overflow-y: auto; } .modal-fullscreen-xxl-down .modal-footer { border-radius: 0; } } .tooltip { position: absolute; z-index: 1080; display: block; margin: 0; font-family: var(--bs-font-sans-serif); font-style: normal; font-weight: 400; line-height: 1.5; text-align: right; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; letter-spacing: normal; word-break: normal; word-spacing: normal; white-space: normal; line-break: auto; font-size: 0.875rem; word-wrap: break-word; opacity: 0; } .tooltip.show { opacity: 0.9; } .tooltip .tooltip-arrow { position: absolute; display: block; width: 0.8rem; height: 0.4rem; } .tooltip .tooltip-arrow::before { position: absolute; content: ""; border-color: transparent; border-style: solid; } .bs-tooltip-top, .bs-tooltip-auto[data-popper-placement^="top"] { padding: 0.4rem 0; } .bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow { bottom: 0; } .bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow::before { top: -1px; border-width: 0.4rem 0.4rem 0; border-top-color: #000; } .bs-tooltip-end, .bs-tooltip-auto[data-popper-placement^="right"] { padding: 0 0.4rem; } .bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow { right: 0; width: 0.4rem; height: 0.8rem; } .bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow::before { left: -1px; border-width: 0.4rem 0 0.4rem 0.4rem; border-left-color: #000; } .bs-tooltip-bottom, .bs-tooltip-auto[data-popper-placement^="bottom"] { padding: 0.4rem 0; } .bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow { top: 0; } .bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow::before { bottom: -1px; border-width: 0 0.4rem 0.4rem; border-bottom-color: #000; } .bs-tooltip-start, .bs-tooltip-auto[data-popper-placement^="left"] { padding: 0 0.4rem; } .bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow { left: 0; width: 0.4rem; height: 0.8rem; } .bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow::before { right: -1px; border-width: 0.4rem 0.4rem 0.4rem 0; border-right-color: #000; } .tooltip-inner { max-width: 200px; padding: 0.25rem 0.5rem; color: #fff; text-align: center; background-color: #000; border-radius: 0.25rem; } .popover { position: absolute; top: 0; left: 0; z-index: 1070; display: block; max-width: 276px; font-family: var(--bs-font-sans-serif); font-style: normal; font-weight: 400; line-height: 1.5; text-align: right; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; letter-spacing: normal; word-break: normal; word-spacing: normal; white-space: normal; line-break: auto; font-size: 0.875rem; word-wrap: break-word; background-color: #fff; background-clip: padding-box; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 0.3rem; } .popover .popover-arrow { position: absolute; display: block; width: 1rem; height: 0.5rem; } .popover .popover-arrow::before, .popover .popover-arrow::after { position: absolute; display: block; content: ""; border-color: transparent; border-style: solid; } .bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^="top"] > .popover-arrow { bottom: calc(-0.5rem - 1px); } .bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^="top"] > .popover-arrow::before { bottom: 0; border-width: 0.5rem 0.5rem 0; border-top-color: rgba(0, 0, 0, 0.25); } .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="top"] > .popover-arrow::after { bottom: 1px; border-width: 0.5rem 0.5rem 0; border-top-color: #fff; } .bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^="right"] > .popover-arrow { right: calc(-0.5rem - 1px); width: 0.5rem; height: 1rem; } .bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^="right"] > .popover-arrow::before { right: 0; border-width: 0.5rem 0 0.5rem 0.5rem; border-left-color: rgba(0, 0, 0, 0.25); } .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="right"] > .popover-arrow::after { right: 1px; border-width: 0.5rem 0 0.5rem 0.5rem; border-left-color: #fff; } .bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow { top: calc(-0.5rem - 1px); } .bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow::before { top: 0; border-width: 0 0.5rem 0.5rem 0.5rem; border-bottom-color: rgba(0, 0, 0, 0.25); } .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow::after { top: 1px; border-width: 0 0.5rem 0.5rem 0.5rem; border-bottom-color: #fff; } .bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^="bottom"] .popover-header::before { position: absolute; top: 0; right: 50%; display: block; width: 1rem; margin-right: -0.5rem; content: ""; border-bottom: 1px solid #f0f0f0; } .bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^="left"] > .popover-arrow { left: calc(-0.5rem - 1px); width: 0.5rem; height: 1rem; } .bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^="left"] > .popover-arrow::before { left: 0; border-width: 0.5rem 0.5rem 0.5rem 0; border-right-color: rgba(0, 0, 0, 0.25); } .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="left"] > .popover-arrow::after { left: 1px; border-width: 0.5rem 0.5rem 0.5rem 0; border-right-color: #fff; } .popover-header { padding: 0.5rem 1rem; margin-bottom: 0; font-size: 1rem; background-color: #f0f0f0; border-bottom: 1px solid rgba(0, 0, 0, 0.2); border-top-right-radius: calc(0.3rem - 1px); border-top-left-radius: calc(0.3rem - 1px); } .popover-header:empty { display: none; } .popover-body { padding: 1rem 1rem; color: #212529; } .carousel { position: relative; } .carousel.pointer-event { touch-action: pan-y; } .carousel-inner { position: relative; width: 100%; overflow: hidden; } .carousel-inner::after { display: block; clear: both; content: ""; } .carousel-item { position: relative; display: none; float: right; width: 100%; margin-left: -100%; -webkit-backface-visibility: hidden; backface-visibility: hidden; transition: transform 0.6s ease-in-out; } @media (prefers-reduced-motion: reduce) { .carousel-item { transition: none; } } .carousel-item.active, .carousel-item-next, .carousel-item-prev { display: block; } .carousel-item-next:not(.carousel-item-start), .active.carousel-item-end { transform: translateX(100%); } .carousel-item-prev:not(.carousel-item-end), .active.carousel-item-start { transform: translateX(-100%); } .carousel-fade .carousel-item { opacity: 0; transition-property: opacity; transform: none; } .carousel-fade .carousel-item.active, .carousel-fade .carousel-item-next.carousel-item-start, .carousel-fade .carousel-item-prev.carousel-item-end { z-index: 1; opacity: 1; } .carousel-fade .active.carousel-item-start, .carousel-fade .active.carousel-item-end { z-index: 0; opacity: 0; transition: opacity 0s 0.6s; } @media (prefers-reduced-motion: reduce) { .carousel-fade .active.carousel-item-start, .carousel-fade .active.carousel-item-end { transition: none; } } .carousel-control-prev, .carousel-control-next { position: absolute; top: 0; bottom: 0; z-index: 1; display: flex; align-items: center; justify-content: center; width: 15%; padding: 0; color: #fff; text-align: center; background: none; border: 0; opacity: 0.5; transition: opacity 0.15s ease; } @media (prefers-reduced-motion: reduce) { .carousel-control-prev, .carousel-control-next { transition: none; } } .carousel-control-prev:hover, .carousel-control-prev:focus, .carousel-control-next:hover, .carousel-control-next:focus { color: #fff; text-decoration: none; outline: 0; opacity: 0.9; } .carousel-control-prev { right: 0; } .carousel-control-next { left: 0; } .carousel-control-prev-icon, .carousel-control-next-icon { display: inline-block; width: 2rem; height: 2rem; background-repeat: no-repeat; background-position: 50%; background-size: 100% 100%; } .carousel-control-next-icon { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e"); } .carousel-control-prev-icon { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); } .carousel-indicators { position: absolute; left: 0; bottom: 0; right: 0; z-index: 2; display: flex; justify-content: center; padding: 0; margin-left: 15%; margin-bottom: 1rem; margin-right: 15%; list-style: none; } .carousel-indicators [data-bs-target] { box-sizing: content-box; flex: 0 1 auto; width: 30px; height: 3px; padding: 0; margin-left: 3px; margin-right: 3px; text-indent: -999px; cursor: pointer; background-color: #fff; background-clip: padding-box; border: 0; border-top: 10px solid transparent; border-bottom: 10px solid transparent; opacity: 0.5; transition: opacity 0.6s ease; } @media (prefers-reduced-motion: reduce) { .carousel-indicators [data-bs-target] { transition: none; } } .carousel-indicators .active { opacity: 1; } .carousel-caption { position: absolute; left: 15%; bottom: 1.25rem; right: 15%; padding-top: 1.25rem; padding-bottom: 1.25rem; color: #fff; text-align: center; } .carousel-dark .carousel-control-next-icon, .carousel-dark .carousel-control-prev-icon { filter: invert(1) grayscale(100); } .carousel-dark .carousel-indicators [data-bs-target] { background-color: #000; } .carousel-dark .carousel-caption { color: #000; } @-webkit-keyframes spinner-border { to { transform: rotate(360deg); } } @keyframes spinner-border { to { transform: rotate(360deg); } } .spinner-border { display: inline-block; width: 2rem; height: 2rem; vertical-align: -0.125em; border: 0.25em solid currentColor; border-left-color: transparent; border-radius: 50%; -webkit-animation: 0.75s linear infinite spinner-border; animation: 0.75s linear infinite spinner-border; } .spinner-border-sm { width: 1rem; height: 1rem; border-width: 0.2em; } @-webkit-keyframes spinner-grow { 0% { transform: scale(0); } 50% { opacity: 1; transform: none; } } @keyframes spinner-grow { 0% { transform: scale(0); } 50% { opacity: 1; transform: none; } } .spinner-grow { display: inline-block; width: 2rem; height: 2rem; vertical-align: -0.125em; background-color: currentColor; border-radius: 50%; opacity: 0; -webkit-animation: 0.75s linear infinite spinner-grow; animation: 0.75s linear infinite spinner-grow; } .spinner-grow-sm { width: 1rem; height: 1rem; } @media (prefers-reduced-motion: reduce) { .spinner-border, .spinner-grow { -webkit-animation-duration: 1.5s; animation-duration: 1.5s; } } .offcanvas { position: fixed; bottom: 0; z-index: 1045; display: flex; flex-direction: column; max-width: 100%; visibility: hidden; background-color: #fff; background-clip: padding-box; outline: 0; transition: transform 0.3s ease-in-out; } @media (prefers-reduced-motion: reduce) { .offcanvas { transition: none; } } .offcanvas-backdrop { position: fixed; top: 0; right: 0; z-index: 1040; width: 100vw; height: 100vh; background-color: #000; } .offcanvas-backdrop.fade { opacity: 0; } .offcanvas-backdrop.show { opacity: 0.5; } .offcanvas-header { display: flex; align-items: center; justify-content: space-between; padding: 1rem 1rem; } .offcanvas-header .btn-close { padding: 0.5rem 0.5rem; margin-top: -0.5rem; margin-left: -0.5rem; margin-bottom: -0.5rem; } .offcanvas-title { margin-bottom: 0; line-height: 1.5; } .offcanvas-body { flex-grow: 1; padding: 1rem 1rem; overflow-y: auto; } .offcanvas-start { top: 0; right: 0; width: 400px; border-left: 1px solid rgba(0, 0, 0, 0.2); transform: translateX(100%); } .offcanvas-end { top: 0; left: 0; width: 400px; border-right: 1px solid rgba(0, 0, 0, 0.2); transform: translateX(-100%); } .offcanvas-top { top: 0; left: 0; right: 0; height: 30vh; max-height: 100%; border-bottom: 1px solid rgba(0, 0, 0, 0.2); transform: translateY(-100%); } .offcanvas-bottom { left: 0; right: 0; height: 30vh; max-height: 100%; border-top: 1px solid rgba(0, 0, 0, 0.2); transform: translateY(100%); } .offcanvas.show { transform: none; } .placeholder { display: inline-block; min-height: 1em; vertical-align: middle; cursor: wait; background-color: currentColor; opacity: 0.5; } .placeholder.btn::before { display: inline-block; content: ""; } .placeholder-xs { min-height: 0.6em; } .placeholder-sm { min-height: 0.8em; } .placeholder-lg { min-height: 1.2em; } .placeholder-glow .placeholder { -webkit-animation: placeholder-glow 2s ease-in-out infinite; animation: placeholder-glow 2s ease-in-out infinite; } @-webkit-keyframes placeholder-glow { 50% { opacity: 0.2; } } @keyframes placeholder-glow { 50% { opacity: 0.2; } } .placeholder-wave { -webkit-mask-image: linear-gradient( 130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95% ); mask-image: linear-gradient( 130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95% ); -webkit-mask-size: 200% 100%; mask-size: 200% 100%; -webkit-animation: placeholder-wave 2s linear infinite; animation: placeholder-wave 2s linear infinite; } @-webkit-keyframes placeholder-wave { 100% { -webkit-mask-position: -200% 0%; mask-position: -200% 0%; } } @keyframes placeholder-wave { 100% { -webkit-mask-position: -200% 0%; mask-position: -200% 0%; } } .clearfix::after { display: block; clear: both; content: ""; } .link-primary { color: #0d6efd; } .link-primary:hover, .link-primary:focus { color: #0a58ca; } .link-secondary { color: #6c757d; } .link-secondary:hover, .link-secondary:focus { color: #565e64; } .link-success { color: #198754; } .link-success:hover, .link-success:focus { color: #146c43; } .link-info { color: #0dcaf0; } .link-info:hover, .link-info:focus { color: #3dd5f3; } .link-warning { color: #ffc107; } .link-warning:hover, .link-warning:focus { color: #ffcd39; } .link-danger { color: #dc3545; } .link-danger:hover, .link-danger:focus { color: #b02a37; } .link-light { color: #f8f9fa; } .link-light:hover, .link-light:focus { color: #f9fafb; } .link-dark { color: #212529; } .link-dark:hover, .link-dark:focus { color: #1a1e21; } .ratio { position: relative; width: 100%; } .ratio::before { display: block; padding-top: var(--bs-aspect-ratio); content: ""; } .ratio > * { position: absolute; top: 0; right: 0; width: 100%; height: 100%; } .ratio-1x1 { --bs-aspect-ratio: 100%; } .ratio-4x3 { --bs-aspect-ratio: 75%; } .ratio-16x9 { --bs-aspect-ratio: 56.25%; } .ratio-21x9 { --bs-aspect-ratio: 42.8571428571%; } .fixed-top { position: fixed; top: 0; left: 0; right: 0; z-index: 1030; } .fixed-bottom { position: fixed; left: 0; bottom: 0; right: 0; z-index: 1030; } .sticky-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } @media (min-width: 576px) { .sticky-sm-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 768px) { .sticky-md-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 992px) { .sticky-lg-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 1200px) { .sticky-xl-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } @media (min-width: 1400px) { .sticky-xxl-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } } .hstack { display: flex; flex-direction: row; align-items: center; align-self: stretch; } .vstack { display: flex; flex: 1 1 auto; flex-direction: column; align-self: stretch; } .visually-hidden, .visually-hidden-focusable:not(:focus):not(:focus-within) { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; border: 0 !important; } .stretched-link::after { position: absolute; top: 0; left: 0; bottom: 0; right: 0; z-index: 1; content: ""; } .text-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .vr { display: inline-block; align-self: stretch; width: 1px; min-height: 1em; background-color: currentColor; opacity: 0.25; } .align-baseline { vertical-align: baseline !important; } .align-top { vertical-align: top !important; } .align-middle { vertical-align: middle !important; } .align-bottom { vertical-align: bottom !important; } .align-text-bottom { vertical-align: text-bottom !important; } .align-text-top { vertical-align: text-top !important; } .float-start { float: right !important; } .float-end { float: left !important; } .float-none { float: none !important; } .opacity-0 { opacity: 0 !important; } .opacity-25 { opacity: 0.25 !important; } .opacity-50 { opacity: 0.5 !important; } .opacity-75 { opacity: 0.75 !important; } .opacity-100 { opacity: 1 !important; } .overflow-auto { overflow: auto !important; } .overflow-hidden { overflow: hidden !important; } .overflow-visible { overflow: visible !important; } .overflow-scroll { overflow: scroll !important; } .d-inline { display: inline !important; } .d-inline-block { display: inline-block !important; } .d-block { display: block !important; } .d-grid { display: grid !important; } .d-table { display: table !important; } .d-table-row { display: table-row !important; } .d-table-cell { display: table-cell !important; } .d-flex { display: flex !important; } .d-inline-flex { display: inline-flex !important; } .d-none { display: none !important; } .shadow { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; } .shadow-sm { box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; } .shadow-lg { box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; } .shadow-none { box-shadow: none !important; } .position-static { position: static !important; } .position-relative { position: relative !important; } .position-absolute { position: absolute !important; } .position-fixed { position: fixed !important; } .position-sticky { position: -webkit-sticky !important; position: sticky !important; } .top-0 { top: 0 !important; } .top-50 { top: 50% !important; } .top-100 { top: 100% !important; } .bottom-0 { bottom: 0 !important; } .bottom-50 { bottom: 50% !important; } .bottom-100 { bottom: 100% !important; } .start-0 { right: 0 !important; } .start-50 { right: 50% !important; } .start-100 { right: 100% !important; } .end-0 { left: 0 !important; } .end-50 { left: 50% !important; } .end-100 { left: 100% !important; } .translate-middle { transform: translate(50%, -50%) !important; } .translate-middle-x { transform: translateX(50%) !important; } .translate-middle-y { transform: translateY(-50%) !important; } .border { border: 1px solid #dee2e6 !important; } .border-0 { border: 0 !important; } .border-top { border-top: 1px solid #dee2e6 !important; } .border-top-0 { border-top: 0 !important; } .border-end { border-left: 1px solid #dee2e6 !important; } .border-end-0 { border-left: 0 !important; } .border-bottom { border-bottom: 1px solid #dee2e6 !important; } .border-bottom-0 { border-bottom: 0 !important; } .border-start { border-right: 1px solid #dee2e6 !important; } .border-start-0 { border-right: 0 !important; } .border-primary { border-color: #0d6efd !important; } .border-secondary { border-color: #6c757d !important; } .border-success { border-color: #198754 !important; } .border-info { border-color: #0dcaf0 !important; } .border-warning { border-color: #ffc107 !important; } .border-danger { border-color: #dc3545 !important; } .border-light { border-color: #f8f9fa !important; } .border-dark { border-color: #212529 !important; } .border-white { border-color: #fff !important; } .border-1 { border-width: 1px !important; } .border-2 { border-width: 2px !important; } .border-3 { border-width: 3px !important; } .border-4 { border-width: 4px !important; } .border-5 { border-width: 5px !important; } .w-25 { width: 25% !important; } .w-50 { width: 50% !important; } .w-75 { width: 75% !important; } .w-100 { width: 100% !important; } .w-auto { width: auto !important; } .mw-100 { max-width: 100% !important; } .vw-100 { width: 100vw !important; } .min-vw-100 { min-width: 100vw !important; } .h-25 { height: 25% !important; } .h-50 { height: 50% !important; } .h-75 { height: 75% !important; } .h-100 { height: 100% !important; } .h-auto { height: auto !important; } .mh-100 { max-height: 100% !important; } .vh-100 { height: 100vh !important; } .min-vh-100 { min-height: 100vh !important; } .flex-fill { flex: 1 1 auto !important; } .flex-row { flex-direction: row !important; } .flex-column { flex-direction: column !important; } .flex-row-reverse { flex-direction: row-reverse !important; } .flex-column-reverse { flex-direction: column-reverse !important; } .flex-grow-0 { flex-grow: 0 !important; } .flex-grow-1 { flex-grow: 1 !important; } .flex-shrink-0 { flex-shrink: 0 !important; } .flex-shrink-1 { flex-shrink: 1 !important; } .flex-wrap { flex-wrap: wrap !important; } .flex-nowrap { flex-wrap: nowrap !important; } .flex-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-0 { gap: 0 !important; } .gap-1 { gap: 0.25rem !important; } .gap-2 { gap: 0.5rem !important; } .gap-3 { gap: 1rem !important; } .gap-4 { gap: 1.5rem !important; } .gap-5 { gap: 3rem !important; } .justify-content-start { justify-content: flex-start !important; } .justify-content-end { justify-content: flex-end !important; } .justify-content-center { justify-content: center !important; } .justify-content-between { justify-content: space-between !important; } .justify-content-around { justify-content: space-around !important; } .justify-content-evenly { justify-content: space-evenly !important; } .align-items-start { align-items: flex-start !important; } .align-items-end { align-items: flex-end !important; } .align-items-center { align-items: center !important; } .align-items-baseline { align-items: baseline !important; } .align-items-stretch { align-items: stretch !important; } .align-content-start { align-content: flex-start !important; } .align-content-end { align-content: flex-end !important; } .align-content-center { align-content: center !important; } .align-content-between { align-content: space-between !important; } .align-content-around { align-content: space-around !important; } .align-content-stretch { align-content: stretch !important; } .align-self-auto { align-self: auto !important; } .align-self-start { align-self: flex-start !important; } .align-self-end { align-self: flex-end !important; } .align-self-center { align-self: center !important; } .align-self-baseline { align-self: baseline !important; } .align-self-stretch { align-self: stretch !important; } .order-first { order: -1 !important; } .order-0 { order: 0 !important; } .order-1 { order: 1 !important; } .order-2 { order: 2 !important; } .order-3 { order: 3 !important; } .order-4 { order: 4 !important; } .order-5 { order: 5 !important; } .order-last { order: 6 !important; } .m-0 { margin: 0 !important; } .m-1 { margin: 0.25rem !important; } .m-2 { margin: 0.5rem !important; } .m-3 { margin: 1rem !important; } .m-4 { margin: 1.5rem !important; } .m-5 { margin: 3rem !important; } .m-auto { margin: auto !important; } .mx-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-auto { margin-left: auto !important; margin-right: auto !important; } .my-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-0 { margin-top: 0 !important; } .mt-1 { margin-top: 0.25rem !important; } .mt-2 { margin-top: 0.5rem !important; } .mt-3 { margin-top: 1rem !important; } .mt-4 { margin-top: 1.5rem !important; } .mt-5 { margin-top: 3rem !important; } .mt-auto { margin-top: auto !important; } .me-0 { margin-left: 0 !important; } .me-1 { margin-left: 0.25rem !important; } .me-2 { margin-left: 0.5rem !important; } .me-3 { margin-left: 1rem !important; } .me-4 { margin-left: 1.5rem !important; } .me-5 { margin-left: 3rem !important; } .me-auto { margin-left: auto !important; } .mb-0 { margin-bottom: 0 !important; } .mb-1 { margin-bottom: 0.25rem !important; } .mb-2 { margin-bottom: 0.5rem !important; } .mb-3 { margin-bottom: 1rem !important; } .mb-4 { margin-bottom: 1.5rem !important; } .mb-5 { margin-bottom: 3rem !important; } .mb-auto { margin-bottom: auto !important; } .ms-0 { margin-right: 0 !important; } .ms-1 { margin-right: 0.25rem !important; } .ms-2 { margin-right: 0.5rem !important; } .ms-3 { margin-right: 1rem !important; } .ms-4 { margin-right: 1.5rem !important; } .ms-5 { margin-right: 3rem !important; } .ms-auto { margin-right: auto !important; } .p-0 { padding: 0 !important; } .p-1 { padding: 0.25rem !important; } .p-2 { padding: 0.5rem !important; } .p-3 { padding: 1rem !important; } .p-4 { padding: 1.5rem !important; } .p-5 { padding: 3rem !important; } .px-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-0 { padding-top: 0 !important; } .pt-1 { padding-top: 0.25rem !important; } .pt-2 { padding-top: 0.5rem !important; } .pt-3 { padding-top: 1rem !important; } .pt-4 { padding-top: 1.5rem !important; } .pt-5 { padding-top: 3rem !important; } .pe-0 { padding-left: 0 !important; } .pe-1 { padding-left: 0.25rem !important; } .pe-2 { padding-left: 0.5rem !important; } .pe-3 { padding-left: 1rem !important; } .pe-4 { padding-left: 1.5rem !important; } .pe-5 { padding-left: 3rem !important; } .pb-0 { padding-bottom: 0 !important; } .pb-1 { padding-bottom: 0.25rem !important; } .pb-2 { padding-bottom: 0.5rem !important; } .pb-3 { padding-bottom: 1rem !important; } .pb-4 { padding-bottom: 1.5rem !important; } .pb-5 { padding-bottom: 3rem !important; } .ps-0 { padding-right: 0 !important; } .ps-1 { padding-right: 0.25rem !important; } .ps-2 { padding-right: 0.5rem !important; } .ps-3 { padding-right: 1rem !important; } .ps-4 { padding-right: 1.5rem !important; } .ps-5 { padding-right: 3rem !important; } .font-monospace { font-family: var(--bs-font-monospace) !important; } .fs-1 { font-size: calc(1.375rem + 1.5vw) !important; } .fs-2 { font-size: calc(1.325rem + 0.9vw) !important; } .fs-3 { font-size: calc(1.3rem + 0.6vw) !important; } .fs-4 { font-size: calc(1.275rem + 0.3vw) !important; } .fs-5 { font-size: 1.25rem !important; } .fs-6 { font-size: 1rem !important; } .fst-italic { font-style: italic !important; } .fst-normal { font-style: normal !important; } .fw-light { font-weight: 300 !important; } .fw-lighter { font-weight: lighter !important; } .fw-normal { font-weight: 400 !important; } .fw-bold { font-weight: 700 !important; } .fw-bolder { font-weight: bolder !important; } .lh-1 { line-height: 1 !important; } .lh-sm { line-height: 1.25 !important; } .lh-base { line-height: 1.5 !important; } .lh-lg { line-height: 2 !important; } .text-start { text-align: right !important; } .text-end { text-align: left !important; } .text-center { text-align: center !important; } .text-decoration-none { text-decoration: none !important; } .text-decoration-underline { text-decoration: underline !important; } .text-decoration-line-through { text-decoration: line-through !important; } .text-lowercase { text-transform: lowercase !important; } .text-uppercase { text-transform: uppercase !important; } .text-capitalize { text-transform: capitalize !important; } .text-wrap { white-space: normal !important; } .text-nowrap { white-space: nowrap !important; } .text-primary { --bs-text-opacity: 1; color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; } .text-secondary { --bs-text-opacity: 1; color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; } .text-success { --bs-text-opacity: 1; color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; } .text-info { --bs-text-opacity: 1; color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; } .text-warning { --bs-text-opacity: 1; color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; } .text-danger { --bs-text-opacity: 1; color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; } .text-light { --bs-text-opacity: 1; color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; } .text-dark { --bs-text-opacity: 1; color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; } .text-black { --bs-text-opacity: 1; color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; } .text-white { --bs-text-opacity: 1; color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; } .text-body { --bs-text-opacity: 1; color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; } .text-muted { --bs-text-opacity: 1; color: #6c757d !important; } .text-black-50 { --bs-text-opacity: 1; color: rgba(0, 0, 0, 0.5) !important; } .text-white-50 { --bs-text-opacity: 1; color: rgba(255, 255, 255, 0.5) !important; } .text-reset { --bs-text-opacity: 1; color: inherit !important; } .text-opacity-25 { --bs-text-opacity: 0.25; } .text-opacity-50 { --bs-text-opacity: 0.5; } .text-opacity-75 { --bs-text-opacity: 0.75; } .text-opacity-100 { --bs-text-opacity: 1; } .bg-primary { --bs-bg-opacity: 1; background-color: rgba( var(--bs-primary-rgb), var(--bs-bg-opacity) ) !important; } .bg-secondary { --bs-bg-opacity: 1; background-color: rgba( var(--bs-secondary-rgb), var(--bs-bg-opacity) ) !important; } .bg-success { --bs-bg-opacity: 1; background-color: rgba( var(--bs-success-rgb), var(--bs-bg-opacity) ) !important; } .bg-info { --bs-bg-opacity: 1; background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; } .bg-warning { --bs-bg-opacity: 1; background-color: rgba( var(--bs-warning-rgb), var(--bs-bg-opacity) ) !important; } .bg-danger { --bs-bg-opacity: 1; background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; } .bg-light { --bs-bg-opacity: 1; background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; } .bg-dark { --bs-bg-opacity: 1; background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } .bg-black { --bs-bg-opacity: 1; background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; } .bg-white { --bs-bg-opacity: 1; background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; } .bg-body { --bs-bg-opacity: 1; background-color: rgba( var(--bs-body-bg-rgb), var(--bs-bg-opacity) ) !important; } .bg-transparent { --bs-bg-opacity: 1; background-color: transparent !important; } .bg-opacity-10 { --bs-bg-opacity: 0.1; } .bg-opacity-25 { --bs-bg-opacity: 0.25; } .bg-opacity-50 { --bs-bg-opacity: 0.5; } .bg-opacity-75 { --bs-bg-opacity: 0.75; } .bg-opacity-100 { --bs-bg-opacity: 1; } .bg-gradient { background-image: var(--bs-gradient) !important; } .user-select-all { -webkit-user-select: all !important; -moz-user-select: all !important; user-select: all !important; } .user-select-auto { -webkit-user-select: auto !important; -moz-user-select: auto !important; user-select: auto !important; } .user-select-none { -webkit-user-select: none !important; -moz-user-select: none !important; user-select: none !important; } .pe-none { pointer-events: none !important; } .pe-auto { pointer-events: auto !important; } .rounded { border-radius: 0.25rem !important; } .rounded-0 { border-radius: 0 !important; } .rounded-1 { border-radius: 0.2rem !important; } .rounded-2 { border-radius: 0.25rem !important; } .rounded-3 { border-radius: 0.3rem !important; } .rounded-circle { border-radius: 50% !important; } .rounded-pill { border-radius: 50rem !important; } .rounded-top { border-top-right-radius: 0.25rem !important; border-top-left-radius: 0.25rem !important; } .rounded-end { border-top-left-radius: 0.25rem !important; border-bottom-left-radius: 0.25rem !important; } .rounded-bottom { border-bottom-left-radius: 0.25rem !important; border-bottom-right-radius: 0.25rem !important; } .rounded-start { border-bottom-right-radius: 0.25rem !important; border-top-right-radius: 0.25rem !important; } .visible { visibility: visible !important; } .invisible { visibility: hidden !important; } @media (min-width: 576px) { .float-sm-start { float: right !important; } .float-sm-end { float: left !important; } .float-sm-none { float: none !important; } .d-sm-inline { display: inline !important; } .d-sm-inline-block { display: inline-block !important; } .d-sm-block { display: block !important; } .d-sm-grid { display: grid !important; } .d-sm-table { display: table !important; } .d-sm-table-row { display: table-row !important; } .d-sm-table-cell { display: table-cell !important; } .d-sm-flex { display: flex !important; } .d-sm-inline-flex { display: inline-flex !important; } .d-sm-none { display: none !important; } .flex-sm-fill { flex: 1 1 auto !important; } .flex-sm-row { flex-direction: row !important; } .flex-sm-column { flex-direction: column !important; } .flex-sm-row-reverse { flex-direction: row-reverse !important; } .flex-sm-column-reverse { flex-direction: column-reverse !important; } .flex-sm-grow-0 { flex-grow: 0 !important; } .flex-sm-grow-1 { flex-grow: 1 !important; } .flex-sm-shrink-0 { flex-shrink: 0 !important; } .flex-sm-shrink-1 { flex-shrink: 1 !important; } .flex-sm-wrap { flex-wrap: wrap !important; } .flex-sm-nowrap { flex-wrap: nowrap !important; } .flex-sm-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-sm-0 { gap: 0 !important; } .gap-sm-1 { gap: 0.25rem !important; } .gap-sm-2 { gap: 0.5rem !important; } .gap-sm-3 { gap: 1rem !important; } .gap-sm-4 { gap: 1.5rem !important; } .gap-sm-5 { gap: 3rem !important; } .justify-content-sm-start { justify-content: flex-start !important; } .justify-content-sm-end { justify-content: flex-end !important; } .justify-content-sm-center { justify-content: center !important; } .justify-content-sm-between { justify-content: space-between !important; } .justify-content-sm-around { justify-content: space-around !important; } .justify-content-sm-evenly { justify-content: space-evenly !important; } .align-items-sm-start { align-items: flex-start !important; } .align-items-sm-end { align-items: flex-end !important; } .align-items-sm-center { align-items: center !important; } .align-items-sm-baseline { align-items: baseline !important; } .align-items-sm-stretch { align-items: stretch !important; } .align-content-sm-start { align-content: flex-start !important; } .align-content-sm-end { align-content: flex-end !important; } .align-content-sm-center { align-content: center !important; } .align-content-sm-between { align-content: space-between !important; } .align-content-sm-around { align-content: space-around !important; } .align-content-sm-stretch { align-content: stretch !important; } .align-self-sm-auto { align-self: auto !important; } .align-self-sm-start { align-self: flex-start !important; } .align-self-sm-end { align-self: flex-end !important; } .align-self-sm-center { align-self: center !important; } .align-self-sm-baseline { align-self: baseline !important; } .align-self-sm-stretch { align-self: stretch !important; } .order-sm-first { order: -1 !important; } .order-sm-0 { order: 0 !important; } .order-sm-1 { order: 1 !important; } .order-sm-2 { order: 2 !important; } .order-sm-3 { order: 3 !important; } .order-sm-4 { order: 4 !important; } .order-sm-5 { order: 5 !important; } .order-sm-last { order: 6 !important; } .m-sm-0 { margin: 0 !important; } .m-sm-1 { margin: 0.25rem !important; } .m-sm-2 { margin: 0.5rem !important; } .m-sm-3 { margin: 1rem !important; } .m-sm-4 { margin: 1.5rem !important; } .m-sm-5 { margin: 3rem !important; } .m-sm-auto { margin: auto !important; } .mx-sm-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-sm-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-sm-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-sm-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-sm-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-sm-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-sm-auto { margin-left: auto !important; margin-right: auto !important; } .my-sm-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-sm-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-sm-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-sm-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-sm-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-sm-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-sm-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-sm-0 { margin-top: 0 !important; } .mt-sm-1 { margin-top: 0.25rem !important; } .mt-sm-2 { margin-top: 0.5rem !important; } .mt-sm-3 { margin-top: 1rem !important; } .mt-sm-4 { margin-top: 1.5rem !important; } .mt-sm-5 { margin-top: 3rem !important; } .mt-sm-auto { margin-top: auto !important; } .me-sm-0 { margin-left: 0 !important; } .me-sm-1 { margin-left: 0.25rem !important; } .me-sm-2 { margin-left: 0.5rem !important; } .me-sm-3 { margin-left: 1rem !important; } .me-sm-4 { margin-left: 1.5rem !important; } .me-sm-5 { margin-left: 3rem !important; } .me-sm-auto { margin-left: auto !important; } .mb-sm-0 { margin-bottom: 0 !important; } .mb-sm-1 { margin-bottom: 0.25rem !important; } .mb-sm-2 { margin-bottom: 0.5rem !important; } .mb-sm-3 { margin-bottom: 1rem !important; } .mb-sm-4 { margin-bottom: 1.5rem !important; } .mb-sm-5 { margin-bottom: 3rem !important; } .mb-sm-auto { margin-bottom: auto !important; } .ms-sm-0 { margin-right: 0 !important; } .ms-sm-1 { margin-right: 0.25rem !important; } .ms-sm-2 { margin-right: 0.5rem !important; } .ms-sm-3 { margin-right: 1rem !important; } .ms-sm-4 { margin-right: 1.5rem !important; } .ms-sm-5 { margin-right: 3rem !important; } .ms-sm-auto { margin-right: auto !important; } .p-sm-0 { padding: 0 !important; } .p-sm-1 { padding: 0.25rem !important; } .p-sm-2 { padding: 0.5rem !important; } .p-sm-3 { padding: 1rem !important; } .p-sm-4 { padding: 1.5rem !important; } .p-sm-5 { padding: 3rem !important; } .px-sm-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-sm-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-sm-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-sm-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-sm-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-sm-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-sm-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-sm-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-sm-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-sm-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-sm-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-sm-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-sm-0 { padding-top: 0 !important; } .pt-sm-1 { padding-top: 0.25rem !important; } .pt-sm-2 { padding-top: 0.5rem !important; } .pt-sm-3 { padding-top: 1rem !important; } .pt-sm-4 { padding-top: 1.5rem !important; } .pt-sm-5 { padding-top: 3rem !important; } .pe-sm-0 { padding-left: 0 !important; } .pe-sm-1 { padding-left: 0.25rem !important; } .pe-sm-2 { padding-left: 0.5rem !important; } .pe-sm-3 { padding-left: 1rem !important; } .pe-sm-4 { padding-left: 1.5rem !important; } .pe-sm-5 { padding-left: 3rem !important; } .pb-sm-0 { padding-bottom: 0 !important; } .pb-sm-1 { padding-bottom: 0.25rem !important; } .pb-sm-2 { padding-bottom: 0.5rem !important; } .pb-sm-3 { padding-bottom: 1rem !important; } .pb-sm-4 { padding-bottom: 1.5rem !important; } .pb-sm-5 { padding-bottom: 3rem !important; } .ps-sm-0 { padding-right: 0 !important; } .ps-sm-1 { padding-right: 0.25rem !important; } .ps-sm-2 { padding-right: 0.5rem !important; } .ps-sm-3 { padding-right: 1rem !important; } .ps-sm-4 { padding-right: 1.5rem !important; } .ps-sm-5 { padding-right: 3rem !important; } .text-sm-start { text-align: right !important; } .text-sm-end { text-align: left !important; } .text-sm-center { text-align: center !important; } } @media (min-width: 768px) { .float-md-start { float: right !important; } .float-md-end { float: left !important; } .float-md-none { float: none !important; } .d-md-inline { display: inline !important; } .d-md-inline-block { display: inline-block !important; } .d-md-block { display: block !important; } .d-md-grid { display: grid !important; } .d-md-table { display: table !important; } .d-md-table-row { display: table-row !important; } .d-md-table-cell { display: table-cell !important; } .d-md-flex { display: flex !important; } .d-md-inline-flex { display: inline-flex !important; } .d-md-none { display: none !important; } .flex-md-fill { flex: 1 1 auto !important; } .flex-md-row { flex-direction: row !important; } .flex-md-column { flex-direction: column !important; } .flex-md-row-reverse { flex-direction: row-reverse !important; } .flex-md-column-reverse { flex-direction: column-reverse !important; } .flex-md-grow-0 { flex-grow: 0 !important; } .flex-md-grow-1 { flex-grow: 1 !important; } .flex-md-shrink-0 { flex-shrink: 0 !important; } .flex-md-shrink-1 { flex-shrink: 1 !important; } .flex-md-wrap { flex-wrap: wrap !important; } .flex-md-nowrap { flex-wrap: nowrap !important; } .flex-md-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-md-0 { gap: 0 !important; } .gap-md-1 { gap: 0.25rem !important; } .gap-md-2 { gap: 0.5rem !important; } .gap-md-3 { gap: 1rem !important; } .gap-md-4 { gap: 1.5rem !important; } .gap-md-5 { gap: 3rem !important; } .justify-content-md-start { justify-content: flex-start !important; } .justify-content-md-end { justify-content: flex-end !important; } .justify-content-md-center { justify-content: center !important; } .justify-content-md-between { justify-content: space-between !important; } .justify-content-md-around { justify-content: space-around !important; } .justify-content-md-evenly { justify-content: space-evenly !important; } .align-items-md-start { align-items: flex-start !important; } .align-items-md-end { align-items: flex-end !important; } .align-items-md-center { align-items: center !important; } .align-items-md-baseline { align-items: baseline !important; } .align-items-md-stretch { align-items: stretch !important; } .align-content-md-start { align-content: flex-start !important; } .align-content-md-end { align-content: flex-end !important; } .align-content-md-center { align-content: center !important; } .align-content-md-between { align-content: space-between !important; } .align-content-md-around { align-content: space-around !important; } .align-content-md-stretch { align-content: stretch !important; } .align-self-md-auto { align-self: auto !important; } .align-self-md-start { align-self: flex-start !important; } .align-self-md-end { align-self: flex-end !important; } .align-self-md-center { align-self: center !important; } .align-self-md-baseline { align-self: baseline !important; } .align-self-md-stretch { align-self: stretch !important; } .order-md-first { order: -1 !important; } .order-md-0 { order: 0 !important; } .order-md-1 { order: 1 !important; } .order-md-2 { order: 2 !important; } .order-md-3 { order: 3 !important; } .order-md-4 { order: 4 !important; } .order-md-5 { order: 5 !important; } .order-md-last { order: 6 !important; } .m-md-0 { margin: 0 !important; } .m-md-1 { margin: 0.25rem !important; } .m-md-2 { margin: 0.5rem !important; } .m-md-3 { margin: 1rem !important; } .m-md-4 { margin: 1.5rem !important; } .m-md-5 { margin: 3rem !important; } .m-md-auto { margin: auto !important; } .mx-md-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-md-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-md-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-md-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-md-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-md-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-md-auto { margin-left: auto !important; margin-right: auto !important; } .my-md-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-md-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-md-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-md-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-md-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-md-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-md-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-md-0 { margin-top: 0 !important; } .mt-md-1 { margin-top: 0.25rem !important; } .mt-md-2 { margin-top: 0.5rem !important; } .mt-md-3 { margin-top: 1rem !important; } .mt-md-4 { margin-top: 1.5rem !important; } .mt-md-5 { margin-top: 3rem !important; } .mt-md-auto { margin-top: auto !important; } .me-md-0 { margin-left: 0 !important; } .me-md-1 { margin-left: 0.25rem !important; } .me-md-2 { margin-left: 0.5rem !important; } .me-md-3 { margin-left: 1rem !important; } .me-md-4 { margin-left: 1.5rem !important; } .me-md-5 { margin-left: 3rem !important; } .me-md-auto { margin-left: auto !important; } .mb-md-0 { margin-bottom: 0 !important; } .mb-md-1 { margin-bottom: 0.25rem !important; } .mb-md-2 { margin-bottom: 0.5rem !important; } .mb-md-3 { margin-bottom: 1rem !important; } .mb-md-4 { margin-bottom: 1.5rem !important; } .mb-md-5 { margin-bottom: 3rem !important; } .mb-md-auto { margin-bottom: auto !important; } .ms-md-0 { margin-right: 0 !important; } .ms-md-1 { margin-right: 0.25rem !important; } .ms-md-2 { margin-right: 0.5rem !important; } .ms-md-3 { margin-right: 1rem !important; } .ms-md-4 { margin-right: 1.5rem !important; } .ms-md-5 { margin-right: 3rem !important; } .ms-md-auto { margin-right: auto !important; } .p-md-0 { padding: 0 !important; } .p-md-1 { padding: 0.25rem !important; } .p-md-2 { padding: 0.5rem !important; } .p-md-3 { padding: 1rem !important; } .p-md-4 { padding: 1.5rem !important; } .p-md-5 { padding: 3rem !important; } .px-md-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-md-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-md-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-md-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-md-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-md-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-md-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-md-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-md-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-md-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-md-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-md-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-md-0 { padding-top: 0 !important; } .pt-md-1 { padding-top: 0.25rem !important; } .pt-md-2 { padding-top: 0.5rem !important; } .pt-md-3 { padding-top: 1rem !important; } .pt-md-4 { padding-top: 1.5rem !important; } .pt-md-5 { padding-top: 3rem !important; } .pe-md-0 { padding-left: 0 !important; } .pe-md-1 { padding-left: 0.25rem !important; } .pe-md-2 { padding-left: 0.5rem !important; } .pe-md-3 { padding-left: 1rem !important; } .pe-md-4 { padding-left: 1.5rem !important; } .pe-md-5 { padding-left: 3rem !important; } .pb-md-0 { padding-bottom: 0 !important; } .pb-md-1 { padding-bottom: 0.25rem !important; } .pb-md-2 { padding-bottom: 0.5rem !important; } .pb-md-3 { padding-bottom: 1rem !important; } .pb-md-4 { padding-bottom: 1.5rem !important; } .pb-md-5 { padding-bottom: 3rem !important; } .ps-md-0 { padding-right: 0 !important; } .ps-md-1 { padding-right: 0.25rem !important; } .ps-md-2 { padding-right: 0.5rem !important; } .ps-md-3 { padding-right: 1rem !important; } .ps-md-4 { padding-right: 1.5rem !important; } .ps-md-5 { padding-right: 3rem !important; } .text-md-start { text-align: right !important; } .text-md-end { text-align: left !important; } .text-md-center { text-align: center !important; } } @media (min-width: 992px) { .float-lg-start { float: right !important; } .float-lg-end { float: left !important; } .float-lg-none { float: none !important; } .d-lg-inline { display: inline !important; } .d-lg-inline-block { display: inline-block !important; } .d-lg-block { display: block !important; } .d-lg-grid { display: grid !important; } .d-lg-table { display: table !important; } .d-lg-table-row { display: table-row !important; } .d-lg-table-cell { display: table-cell !important; } .d-lg-flex { display: flex !important; } .d-lg-inline-flex { display: inline-flex !important; } .d-lg-none { display: none !important; } .flex-lg-fill { flex: 1 1 auto !important; } .flex-lg-row { flex-direction: row !important; } .flex-lg-column { flex-direction: column !important; } .flex-lg-row-reverse { flex-direction: row-reverse !important; } .flex-lg-column-reverse { flex-direction: column-reverse !important; } .flex-lg-grow-0 { flex-grow: 0 !important; } .flex-lg-grow-1 { flex-grow: 1 !important; } .flex-lg-shrink-0 { flex-shrink: 0 !important; } .flex-lg-shrink-1 { flex-shrink: 1 !important; } .flex-lg-wrap { flex-wrap: wrap !important; } .flex-lg-nowrap { flex-wrap: nowrap !important; } .flex-lg-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-lg-0 { gap: 0 !important; } .gap-lg-1 { gap: 0.25rem !important; } .gap-lg-2 { gap: 0.5rem !important; } .gap-lg-3 { gap: 1rem !important; } .gap-lg-4 { gap: 1.5rem !important; } .gap-lg-5 { gap: 3rem !important; } .justify-content-lg-start { justify-content: flex-start !important; } .justify-content-lg-end { justify-content: flex-end !important; } .justify-content-lg-center { justify-content: center !important; } .justify-content-lg-between { justify-content: space-between !important; } .justify-content-lg-around { justify-content: space-around !important; } .justify-content-lg-evenly { justify-content: space-evenly !important; } .align-items-lg-start { align-items: flex-start !important; } .align-items-lg-end { align-items: flex-end !important; } .align-items-lg-center { align-items: center !important; } .align-items-lg-baseline { align-items: baseline !important; } .align-items-lg-stretch { align-items: stretch !important; } .align-content-lg-start { align-content: flex-start !important; } .align-content-lg-end { align-content: flex-end !important; } .align-content-lg-center { align-content: center !important; } .align-content-lg-between { align-content: space-between !important; } .align-content-lg-around { align-content: space-around !important; } .align-content-lg-stretch { align-content: stretch !important; } .align-self-lg-auto { align-self: auto !important; } .align-self-lg-start { align-self: flex-start !important; } .align-self-lg-end { align-self: flex-end !important; } .align-self-lg-center { align-self: center !important; } .align-self-lg-baseline { align-self: baseline !important; } .align-self-lg-stretch { align-self: stretch !important; } .order-lg-first { order: -1 !important; } .order-lg-0 { order: 0 !important; } .order-lg-1 { order: 1 !important; } .order-lg-2 { order: 2 !important; } .order-lg-3 { order: 3 !important; } .order-lg-4 { order: 4 !important; } .order-lg-5 { order: 5 !important; } .order-lg-last { order: 6 !important; } .m-lg-0 { margin: 0 !important; } .m-lg-1 { margin: 0.25rem !important; } .m-lg-2 { margin: 0.5rem !important; } .m-lg-3 { margin: 1rem !important; } .m-lg-4 { margin: 1.5rem !important; } .m-lg-5 { margin: 3rem !important; } .m-lg-auto { margin: auto !important; } .mx-lg-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-lg-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-lg-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-lg-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-lg-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-lg-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-lg-auto { margin-left: auto !important; margin-right: auto !important; } .my-lg-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-lg-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-lg-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-lg-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-lg-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-lg-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-lg-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-lg-0 { margin-top: 0 !important; } .mt-lg-1 { margin-top: 0.25rem !important; } .mt-lg-2 { margin-top: 0.5rem !important; } .mt-lg-3 { margin-top: 1rem !important; } .mt-lg-4 { margin-top: 1.5rem !important; } .mt-lg-5 { margin-top: 3rem !important; } .mt-lg-auto { margin-top: auto !important; } .me-lg-0 { margin-left: 0 !important; } .me-lg-1 { margin-left: 0.25rem !important; } .me-lg-2 { margin-left: 0.5rem !important; } .me-lg-3 { margin-left: 1rem !important; } .me-lg-4 { margin-left: 1.5rem !important; } .me-lg-5 { margin-left: 3rem !important; } .me-lg-auto { margin-left: auto !important; } .mb-lg-0 { margin-bottom: 0 !important; } .mb-lg-1 { margin-bottom: 0.25rem !important; } .mb-lg-2 { margin-bottom: 0.5rem !important; } .mb-lg-3 { margin-bottom: 1rem !important; } .mb-lg-4 { margin-bottom: 1.5rem !important; } .mb-lg-5 { margin-bottom: 3rem !important; } .mb-lg-auto { margin-bottom: auto !important; } .ms-lg-0 { margin-right: 0 !important; } .ms-lg-1 { margin-right: 0.25rem !important; } .ms-lg-2 { margin-right: 0.5rem !important; } .ms-lg-3 { margin-right: 1rem !important; } .ms-lg-4 { margin-right: 1.5rem !important; } .ms-lg-5 { margin-right: 3rem !important; } .ms-lg-auto { margin-right: auto !important; } .p-lg-0 { padding: 0 !important; } .p-lg-1 { padding: 0.25rem !important; } .p-lg-2 { padding: 0.5rem !important; } .p-lg-3 { padding: 1rem !important; } .p-lg-4 { padding: 1.5rem !important; } .p-lg-5 { padding: 3rem !important; } .px-lg-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-lg-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-lg-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-lg-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-lg-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-lg-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-lg-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-lg-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-lg-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-lg-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-lg-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-lg-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-lg-0 { padding-top: 0 !important; } .pt-lg-1 { padding-top: 0.25rem !important; } .pt-lg-2 { padding-top: 0.5rem !important; } .pt-lg-3 { padding-top: 1rem !important; } .pt-lg-4 { padding-top: 1.5rem !important; } .pt-lg-5 { padding-top: 3rem !important; } .pe-lg-0 { padding-left: 0 !important; } .pe-lg-1 { padding-left: 0.25rem !important; } .pe-lg-2 { padding-left: 0.5rem !important; } .pe-lg-3 { padding-left: 1rem !important; } .pe-lg-4 { padding-left: 1.5rem !important; } .pe-lg-5 { padding-left: 3rem !important; } .pb-lg-0 { padding-bottom: 0 !important; } .pb-lg-1 { padding-bottom: 0.25rem !important; } .pb-lg-2 { padding-bottom: 0.5rem !important; } .pb-lg-3 { padding-bottom: 1rem !important; } .pb-lg-4 { padding-bottom: 1.5rem !important; } .pb-lg-5 { padding-bottom: 3rem !important; } .ps-lg-0 { padding-right: 0 !important; } .ps-lg-1 { padding-right: 0.25rem !important; } .ps-lg-2 { padding-right: 0.5rem !important; } .ps-lg-3 { padding-right: 1rem !important; } .ps-lg-4 { padding-right: 1.5rem !important; } .ps-lg-5 { padding-right: 3rem !important; } .text-lg-start { text-align: right !important; } .text-lg-end { text-align: left !important; } .text-lg-center { text-align: center !important; } } @media (min-width: 1200px) { .float-xl-start { float: right !important; } .float-xl-end { float: left !important; } .float-xl-none { float: none !important; } .d-xl-inline { display: inline !important; } .d-xl-inline-block { display: inline-block !important; } .d-xl-block { display: block !important; } .d-xl-grid { display: grid !important; } .d-xl-table { display: table !important; } .d-xl-table-row { display: table-row !important; } .d-xl-table-cell { display: table-cell !important; } .d-xl-flex { display: flex !important; } .d-xl-inline-flex { display: inline-flex !important; } .d-xl-none { display: none !important; } .flex-xl-fill { flex: 1 1 auto !important; } .flex-xl-row { flex-direction: row !important; } .flex-xl-column { flex-direction: column !important; } .flex-xl-row-reverse { flex-direction: row-reverse !important; } .flex-xl-column-reverse { flex-direction: column-reverse !important; } .flex-xl-grow-0 { flex-grow: 0 !important; } .flex-xl-grow-1 { flex-grow: 1 !important; } .flex-xl-shrink-0 { flex-shrink: 0 !important; } .flex-xl-shrink-1 { flex-shrink: 1 !important; } .flex-xl-wrap { flex-wrap: wrap !important; } .flex-xl-nowrap { flex-wrap: nowrap !important; } .flex-xl-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-xl-0 { gap: 0 !important; } .gap-xl-1 { gap: 0.25rem !important; } .gap-xl-2 { gap: 0.5rem !important; } .gap-xl-3 { gap: 1rem !important; } .gap-xl-4 { gap: 1.5rem !important; } .gap-xl-5 { gap: 3rem !important; } .justify-content-xl-start { justify-content: flex-start !important; } .justify-content-xl-end { justify-content: flex-end !important; } .justify-content-xl-center { justify-content: center !important; } .justify-content-xl-between { justify-content: space-between !important; } .justify-content-xl-around { justify-content: space-around !important; } .justify-content-xl-evenly { justify-content: space-evenly !important; } .align-items-xl-start { align-items: flex-start !important; } .align-items-xl-end { align-items: flex-end !important; } .align-items-xl-center { align-items: center !important; } .align-items-xl-baseline { align-items: baseline !important; } .align-items-xl-stretch { align-items: stretch !important; } .align-content-xl-start { align-content: flex-start !important; } .align-content-xl-end { align-content: flex-end !important; } .align-content-xl-center { align-content: center !important; } .align-content-xl-between { align-content: space-between !important; } .align-content-xl-around { align-content: space-around !important; } .align-content-xl-stretch { align-content: stretch !important; } .align-self-xl-auto { align-self: auto !important; } .align-self-xl-start { align-self: flex-start !important; } .align-self-xl-end { align-self: flex-end !important; } .align-self-xl-center { align-self: center !important; } .align-self-xl-baseline { align-self: baseline !important; } .align-self-xl-stretch { align-self: stretch !important; } .order-xl-first { order: -1 !important; } .order-xl-0 { order: 0 !important; } .order-xl-1 { order: 1 !important; } .order-xl-2 { order: 2 !important; } .order-xl-3 { order: 3 !important; } .order-xl-4 { order: 4 !important; } .order-xl-5 { order: 5 !important; } .order-xl-last { order: 6 !important; } .m-xl-0 { margin: 0 !important; } .m-xl-1 { margin: 0.25rem !important; } .m-xl-2 { margin: 0.5rem !important; } .m-xl-3 { margin: 1rem !important; } .m-xl-4 { margin: 1.5rem !important; } .m-xl-5 { margin: 3rem !important; } .m-xl-auto { margin: auto !important; } .mx-xl-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-xl-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-xl-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-xl-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-xl-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-xl-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-xl-auto { margin-left: auto !important; margin-right: auto !important; } .my-xl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xl-0 { margin-top: 0 !important; } .mt-xl-1 { margin-top: 0.25rem !important; } .mt-xl-2 { margin-top: 0.5rem !important; } .mt-xl-3 { margin-top: 1rem !important; } .mt-xl-4 { margin-top: 1.5rem !important; } .mt-xl-5 { margin-top: 3rem !important; } .mt-xl-auto { margin-top: auto !important; } .me-xl-0 { margin-left: 0 !important; } .me-xl-1 { margin-left: 0.25rem !important; } .me-xl-2 { margin-left: 0.5rem !important; } .me-xl-3 { margin-left: 1rem !important; } .me-xl-4 { margin-left: 1.5rem !important; } .me-xl-5 { margin-left: 3rem !important; } .me-xl-auto { margin-left: auto !important; } .mb-xl-0 { margin-bottom: 0 !important; } .mb-xl-1 { margin-bottom: 0.25rem !important; } .mb-xl-2 { margin-bottom: 0.5rem !important; } .mb-xl-3 { margin-bottom: 1rem !important; } .mb-xl-4 { margin-bottom: 1.5rem !important; } .mb-xl-5 { margin-bottom: 3rem !important; } .mb-xl-auto { margin-bottom: auto !important; } .ms-xl-0 { margin-right: 0 !important; } .ms-xl-1 { margin-right: 0.25rem !important; } .ms-xl-2 { margin-right: 0.5rem !important; } .ms-xl-3 { margin-right: 1rem !important; } .ms-xl-4 { margin-right: 1.5rem !important; } .ms-xl-5 { margin-right: 3rem !important; } .ms-xl-auto { margin-right: auto !important; } .p-xl-0 { padding: 0 !important; } .p-xl-1 { padding: 0.25rem !important; } .p-xl-2 { padding: 0.5rem !important; } .p-xl-3 { padding: 1rem !important; } .p-xl-4 { padding: 1.5rem !important; } .p-xl-5 { padding: 3rem !important; } .px-xl-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-xl-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-xl-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-xl-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-xl-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-xl-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-xl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xl-0 { padding-top: 0 !important; } .pt-xl-1 { padding-top: 0.25rem !important; } .pt-xl-2 { padding-top: 0.5rem !important; } .pt-xl-3 { padding-top: 1rem !important; } .pt-xl-4 { padding-top: 1.5rem !important; } .pt-xl-5 { padding-top: 3rem !important; } .pe-xl-0 { padding-left: 0 !important; } .pe-xl-1 { padding-left: 0.25rem !important; } .pe-xl-2 { padding-left: 0.5rem !important; } .pe-xl-3 { padding-left: 1rem !important; } .pe-xl-4 { padding-left: 1.5rem !important; } .pe-xl-5 { padding-left: 3rem !important; } .pb-xl-0 { padding-bottom: 0 !important; } .pb-xl-1 { padding-bottom: 0.25rem !important; } .pb-xl-2 { padding-bottom: 0.5rem !important; } .pb-xl-3 { padding-bottom: 1rem !important; } .pb-xl-4 { padding-bottom: 1.5rem !important; } .pb-xl-5 { padding-bottom: 3rem !important; } .ps-xl-0 { padding-right: 0 !important; } .ps-xl-1 { padding-right: 0.25rem !important; } .ps-xl-2 { padding-right: 0.5rem !important; } .ps-xl-3 { padding-right: 1rem !important; } .ps-xl-4 { padding-right: 1.5rem !important; } .ps-xl-5 { padding-right: 3rem !important; } .text-xl-start { text-align: right !important; } .text-xl-end { text-align: left !important; } .text-xl-center { text-align: center !important; } } @media (min-width: 1400px) { .float-xxl-start { float: right !important; } .float-xxl-end { float: left !important; } .float-xxl-none { float: none !important; } .d-xxl-inline { display: inline !important; } .d-xxl-inline-block { display: inline-block !important; } .d-xxl-block { display: block !important; } .d-xxl-grid { display: grid !important; } .d-xxl-table { display: table !important; } .d-xxl-table-row { display: table-row !important; } .d-xxl-table-cell { display: table-cell !important; } .d-xxl-flex { display: flex !important; } .d-xxl-inline-flex { display: inline-flex !important; } .d-xxl-none { display: none !important; } .flex-xxl-fill { flex: 1 1 auto !important; } .flex-xxl-row { flex-direction: row !important; } .flex-xxl-column { flex-direction: column !important; } .flex-xxl-row-reverse { flex-direction: row-reverse !important; } .flex-xxl-column-reverse { flex-direction: column-reverse !important; } .flex-xxl-grow-0 { flex-grow: 0 !important; } .flex-xxl-grow-1 { flex-grow: 1 !important; } .flex-xxl-shrink-0 { flex-shrink: 0 !important; } .flex-xxl-shrink-1 { flex-shrink: 1 !important; } .flex-xxl-wrap { flex-wrap: wrap !important; } .flex-xxl-nowrap { flex-wrap: nowrap !important; } .flex-xxl-wrap-reverse { flex-wrap: wrap-reverse !important; } .gap-xxl-0 { gap: 0 !important; } .gap-xxl-1 { gap: 0.25rem !important; } .gap-xxl-2 { gap: 0.5rem !important; } .gap-xxl-3 { gap: 1rem !important; } .gap-xxl-4 { gap: 1.5rem !important; } .gap-xxl-5 { gap: 3rem !important; } .justify-content-xxl-start { justify-content: flex-start !important; } .justify-content-xxl-end { justify-content: flex-end !important; } .justify-content-xxl-center { justify-content: center !important; } .justify-content-xxl-between { justify-content: space-between !important; } .justify-content-xxl-around { justify-content: space-around !important; } .justify-content-xxl-evenly { justify-content: space-evenly !important; } .align-items-xxl-start { align-items: flex-start !important; } .align-items-xxl-end { align-items: flex-end !important; } .align-items-xxl-center { align-items: center !important; } .align-items-xxl-baseline { align-items: baseline !important; } .align-items-xxl-stretch { align-items: stretch !important; } .align-content-xxl-start { align-content: flex-start !important; } .align-content-xxl-end { align-content: flex-end !important; } .align-content-xxl-center { align-content: center !important; } .align-content-xxl-between { align-content: space-between !important; } .align-content-xxl-around { align-content: space-around !important; } .align-content-xxl-stretch { align-content: stretch !important; } .align-self-xxl-auto { align-self: auto !important; } .align-self-xxl-start { align-self: flex-start !important; } .align-self-xxl-end { align-self: flex-end !important; } .align-self-xxl-center { align-self: center !important; } .align-self-xxl-baseline { align-self: baseline !important; } .align-self-xxl-stretch { align-self: stretch !important; } .order-xxl-first { order: -1 !important; } .order-xxl-0 { order: 0 !important; } .order-xxl-1 { order: 1 !important; } .order-xxl-2 { order: 2 !important; } .order-xxl-3 { order: 3 !important; } .order-xxl-4 { order: 4 !important; } .order-xxl-5 { order: 5 !important; } .order-xxl-last { order: 6 !important; } .m-xxl-0 { margin: 0 !important; } .m-xxl-1 { margin: 0.25rem !important; } .m-xxl-2 { margin: 0.5rem !important; } .m-xxl-3 { margin: 1rem !important; } .m-xxl-4 { margin: 1.5rem !important; } .m-xxl-5 { margin: 3rem !important; } .m-xxl-auto { margin: auto !important; } .mx-xxl-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-xxl-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; } .mx-xxl-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; } .mx-xxl-3 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-xxl-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; } .mx-xxl-5 { margin-left: 3rem !important; margin-right: 3rem !important; } .mx-xxl-auto { margin-left: auto !important; margin-right: auto !important; } .my-xxl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xxl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xxl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xxl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xxl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xxl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xxl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xxl-0 { margin-top: 0 !important; } .mt-xxl-1 { margin-top: 0.25rem !important; } .mt-xxl-2 { margin-top: 0.5rem !important; } .mt-xxl-3 { margin-top: 1rem !important; } .mt-xxl-4 { margin-top: 1.5rem !important; } .mt-xxl-5 { margin-top: 3rem !important; } .mt-xxl-auto { margin-top: auto !important; } .me-xxl-0 { margin-left: 0 !important; } .me-xxl-1 { margin-left: 0.25rem !important; } .me-xxl-2 { margin-left: 0.5rem !important; } .me-xxl-3 { margin-left: 1rem !important; } .me-xxl-4 { margin-left: 1.5rem !important; } .me-xxl-5 { margin-left: 3rem !important; } .me-xxl-auto { margin-left: auto !important; } .mb-xxl-0 { margin-bottom: 0 !important; } .mb-xxl-1 { margin-bottom: 0.25rem !important; } .mb-xxl-2 { margin-bottom: 0.5rem !important; } .mb-xxl-3 { margin-bottom: 1rem !important; } .mb-xxl-4 { margin-bottom: 1.5rem !important; } .mb-xxl-5 { margin-bottom: 3rem !important; } .mb-xxl-auto { margin-bottom: auto !important; } .ms-xxl-0 { margin-right: 0 !important; } .ms-xxl-1 { margin-right: 0.25rem !important; } .ms-xxl-2 { margin-right: 0.5rem !important; } .ms-xxl-3 { margin-right: 1rem !important; } .ms-xxl-4 { margin-right: 1.5rem !important; } .ms-xxl-5 { margin-right: 3rem !important; } .ms-xxl-auto { margin-right: auto !important; } .p-xxl-0 { padding: 0 !important; } .p-xxl-1 { padding: 0.25rem !important; } .p-xxl-2 { padding: 0.5rem !important; } .p-xxl-3 { padding: 1rem !important; } .p-xxl-4 { padding: 1.5rem !important; } .p-xxl-5 { padding: 3rem !important; } .px-xxl-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-xxl-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; } .px-xxl-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; } .px-xxl-3 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-xxl-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; } .px-xxl-5 { padding-left: 3rem !important; padding-right: 3rem !important; } .py-xxl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xxl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xxl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xxl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xxl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xxl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xxl-0 { padding-top: 0 !important; } .pt-xxl-1 { padding-top: 0.25rem !important; } .pt-xxl-2 { padding-top: 0.5rem !important; } .pt-xxl-3 { padding-top: 1rem !important; } .pt-xxl-4 { padding-top: 1.5rem !important; } .pt-xxl-5 { padding-top: 3rem !important; } .pe-xxl-0 { padding-left: 0 !important; } .pe-xxl-1 { padding-left: 0.25rem !important; } .pe-xxl-2 { padding-left: 0.5rem !important; } .pe-xxl-3 { padding-left: 1rem !important; } .pe-xxl-4 { padding-left: 1.5rem !important; } .pe-xxl-5 { padding-left: 3rem !important; } .pb-xxl-0 { padding-bottom: 0 !important; } .pb-xxl-1 { padding-bottom: 0.25rem !important; } .pb-xxl-2 { padding-bottom: 0.5rem !important; } .pb-xxl-3 { padding-bottom: 1rem !important; } .pb-xxl-4 { padding-bottom: 1.5rem !important; } .pb-xxl-5 { padding-bottom: 3rem !important; } .ps-xxl-0 { padding-right: 0 !important; } .ps-xxl-1 { padding-right: 0.25rem !important; } .ps-xxl-2 { padding-right: 0.5rem !important; } .ps-xxl-3 { padding-right: 1rem !important; } .ps-xxl-4 { padding-right: 1.5rem !important; } .ps-xxl-5 { padding-right: 3rem !important; } .text-xxl-start { text-align: right !important; } .text-xxl-end { text-align: left !important; } .text-xxl-center { text-align: center !important; } } @media (min-width: 1200px) { .fs-1 { font-size: 2.5rem !important; } .fs-2 { font-size: 2rem !important; } .fs-3 { font-size: 1.75rem !important; } .fs-4 { font-size: 1.5rem !important; } } @media print { .d-print-inline { display: inline !important; } .d-print-inline-block { display: inline-block !important; } .d-print-block { display: block !important; } .d-print-grid { display: grid !important; } .d-print-table { display: table !important; } .d-print-table-row { display: table-row !important; } .d-print-table-cell { display: table-cell !important; } .d-print-flex { display: flex !important; } .d-print-inline-flex { display: inline-flex !important; } .d-print-none { display: none !important; } } /*# sourceMappingURL=bootstrap.rtl.css.map */ ================================================ FILE: examples/tailwind-and-bootstrap-example/package.json ================================================ { "name": "tailwind-and-bootstrap-example", "version": "18.5.6", "private": true, "description": "", "main": "index.js", "scripts": { "build": "yarn tailwindcss -i ./src/tailwind.scss -o ./css-modules/tailwind.css" }, "author": "", "license": "ISC", "devDependencies": { "@paperclip-ui/cli": "^18.5.6", "@paperclip-ui/core": "^18.5.6" } } ================================================ FILE: examples/tailwind-and-bootstrap-example/paperclip.config.json ================================================ { "compilerOptions": [ { "outDir": "./lib" } ], "srcDir": "./src", "moduleDirs": ["./css-modules"] } ================================================ FILE: examples/tailwind-and-bootstrap-example/src/hello-paperclip.pc ================================================

    Legacy buttons

    New buttons

    ================================================ FILE: examples/tailwind-and-bootstrap-example/src/tailwind.scss ================================================ @tailwind base; @tailwind components; @tailwind utilities; ================================================ FILE: examples/tailwind-and-bootstrap-example/tailwind.config.js ================================================ module.exports = { content: ["./src/**/*.pc"], theme: { extend: {} }, plugins: [] }; ================================================ FILE: lerna.json ================================================ { "packages": [ "packages/*", "examples/*" ], "version": "18.5.6", "npmClient": "yarn", "useWorkspaces": true, "nohoist": [ "@docusaurus/*", "paperclip-vscode/*", "@paperclip-ui/website/*" ] } ================================================ FILE: package.json ================================================ { "name": "@paperclip-ui/workspaces", "version": "18.0.3", "displayName": "Paperclip", "description": "Paperclip tooling for VS Code", "private": true, "main": "index.js", "directories": { "doc": "docs" }, "engines": { "yarn": "^1.22.17" }, "workspaces": { "packages": [ "packages/*", "examples/*" ], "nohoist": [ "**/@docusaurus", "**/@docusaurus/**", "**/jest", "**/jest/**", "**/mocha", "**/mocha/**", "**/@testing-library/jest-dom/**", "**/@testing-library/jest-dom" ] }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^3.6.1", "@typescript-eslint/parser": "^3.6.1", "codecov": "^3.7.1", "eslint": "^7.4.0", "husky": "^7.0.0", "lerna": "^4.0.0", "prettier": "2.5.1", "pretty-quick": "^3.1.3", "typescript": "^4.2.3" }, "resolutions": { "vscode-languageserver-protocol": "3.15.3" }, "scripts": { "test": "lerna run test", "build": "lerna run build", "lint": "lerna run lint", "link": "lerna run link", "clean": "find ./ -name node_modules -exec rm -rf {} \\;", "clean:hard": "rm -rf yarn.lock && yarn clean", "build:watch": "lerna run build:watch --parallel", "publish:packages": "lerna publish from-package --yes", "publish:marketplace": "lerna run publish:marketplace", "publish:documentation": "lerna run publish:documentation", "version22": "lerna clean --yes && lerna bootstrap --ignore-scripts -- --no-audit", "prepare": "husky install" }, "repository": { "type": "git", "url": "git+https://github.com/paperclipui/paperclip.git" }, "author": "", "license": "", "bugs": { "url": "https://github.com/paperclipui/paperclip/issues" }, "homepage": "https://github.com/paperclipui/paperclip#readme", "packageManager": "yarn@1.22.17" } ================================================ FILE: packages/avocode-paperclip/.gitignore ================================================ config.json ================================================ FILE: packages/avocode-paperclip/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/avocode-paperclip/package.json ================================================ { "name": "avocode-paperclip", "version": "18.4.7", "description": "", "main": "index.js", "publishConfig": { "access": "public" }, "scripts": { "build": "echo ''" }, "author": "", "license": "ISC", "dependencies": { "typescript": "^4.2.3" }, "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a" } ================================================ FILE: packages/avocode-paperclip/src/api.ts ================================================ export class AvocodeClient { constructor(readonly apiKey: string) { } } ================================================ FILE: packages/avocode-paperclip/src/index.ts ================================================ ================================================ FILE: packages/avocode-paperclip/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./lib", "target": "es5", "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true }, "exclude": [ "**/*.d.ts", "lib/**/*.d.ts", "esm", "native", "lib/**/*", "node_modules", "*-test", "index.d.ts", "browser.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/figma-paperclip/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/figma-paperclip/bin/figma-paperclip ================================================ #!/usr/bin/env node const {pull, init} = require("../lib"); const path = require("path"); const argv = require("yargs") .command("pull", "Pull latest designs", yargs => { yargs .option("token", { description: "your Figma token", required: true }) }, argv => { const cwd = process.cwd(); pull({ cwd, token: argv.token }); }).command("init", "Init Paperclip Figma project", yargs => { }, argv => { const cwd = process.cwd(); init({ cwd }); }).argv; ================================================ FILE: packages/figma-paperclip/examples/wrapper-module/.gitignore ================================================ lib src *.graph-cache *.cache ================================================ FILE: packages/figma-paperclip/examples/wrapper-module/figma-paperclip.config.js ================================================ module.exports = { sources: [ "https://www.figma.com/file/0nwtYJeLuHReEfVPr4pHwZ/Untitled?node-id=0%3A1" ], atoms: { prefix: "$", globalVars: true }, outputDir: "src" }; ================================================ FILE: packages/figma-paperclip/examples/wrapper-module/figma-paperclip.json.backup ================================================ { "sources": [ "https://www.figma.com/file/IujaJUC9EUItf5dai5TB4g/Color?node-id=3%3A132" ], "outputDir": "src" } ================================================ FILE: packages/figma-paperclip/examples/wrapper-module/package.json ================================================ { "name": "wrapper-module", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "pull": "../../bin/figma-paperclip pull --token=199110-84ad649f-a49c-41a7-9b00-a672bb4772bf" }, "author": "", "license": "ISC" } ================================================ FILE: packages/figma-paperclip/examples/wrapper-module/paperclip.config.json ================================================ { "srcDir": "./src", "moduleDirs": ["../../node_modules", "../..", "node_modules"] } ================================================ FILE: packages/figma-paperclip/package.json ================================================ { "name": "figma-paperclip", "version": "18.4.7", "description": "", "main": "index.js", "bin": "./bin/figma-paperclip", "publishConfig": { "access": "public" }, "scripts": { "build:watch": "tsc --watch --preserveWatchOutput", "build": "tsc" }, "author": "", "license": "ISC", "devDependencies": { "typescript": "^4.2.4" }, "dependencies": { "chalk": "^4.1.1", "fs-extra": "^10.0.0", "inquirer": "^8.0.0", "lodash": "^4.17.21", "lru-cache": "^6.0.0", "p-limit": "^3.1.0", "tinycolor2": "^1.4.2", "yargs": "^17.0.1" }, "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a" } ================================================ FILE: packages/figma-paperclip/src/api.ts ================================================ import * as https from "https"; import * as qs from "querystring"; import { httpGet } from "./utils"; export class FigmaApi { private _limits = {}; constructor(readonly personalAccessToken: string) {} getFile(fileKey: string, options: any = {}) { return this._get( `/v1/files/${fileKey}`, { geometry: "paths", ...options }, "getFile" ); } getStyles(fileKey: string, options: any = {}) { return this._get(`/v1/files/${fileKey}/styles`, options, "getStyles"); } getStyle(styleKey: string, options: any = {}) { return this._get(`/v1/styles/${styleKey}`, options, "getStyles"); } getVersions(fileKey: string) { return this._get(`/v1/version/${fileKey}`, {}, "getVersions"); } getTeamProjects(teamId: string) { return this._get(`/v1/teams/${teamId}/projects`, {}, "getTeamProjects"); } getProjectFiles(projectId: number) { return this._get(`/v1/projects/${projectId}/files`, {}, "getProjectFiles"); } getImage(fileKey: string, options: any) { return this._get(`/v1/images/${fileKey}`, options, "getImage"); } getComponent(key: string) { return this._get(`/v1/components/${key}`, {}, "getComponent"); } getImageFills(key: string) { return this._get(`/v1/files/${key}/images`, {}, "getImageFills"); } private _get( pathname: string, query: Record = {}, limitName: string ) { if (!this._limits[limitName]) { this._limits[limitName] = figmaRateLimiter(); } const limit = this._limits[limitName]; return limit(async retry => { const search = Object.keys(query).length ? "?" + qs.stringify(query) : ""; try { return await httpGet({ headers: { "X-FIGMA-TOKEN": this.personalAccessToken }, hostname: "api.figma.com", path: pathname + search }); } catch (e) { if (e.status === 429) { return await retry(); } else { throw e; } } }); } } const figmaRateLimiter = () => { const run = (cb: (retry) => Promise) => { const retry = () => { return new Promise((resolve, reject) => { setTimeout(() => { call().then(resolve, reject); }, 1000 * 60); }); }; const call = () => new Promise((resolve, reject) => { cb(retry).then(resolve, reject); }); return call(); }; return run; }; ================================================ FILE: packages/figma-paperclip/src/constants.ts ================================================ import { ExportSettings } from "./state"; export const CONFIG_FILE_NAME = "figmark.json"; export const PC_CONFIG_FILE_NAME = "pcconfig.json"; export const PC_FILE_EXTENSION = ".pc"; export const DEPENDENCIES_NAMESPACE = "dependencies"; export const DEFAULT_COMPILER_TARGET_NAME = "@paperclip-ui/compiler-react"; export const DEFAULT_EXPORT_SETTINGS: ExportSettings = { suffix: "", format: "SVG", constraint: { value: 1, type: "SCALE" } }; export const EMPTY_ARRAY = []; ================================================ FILE: packages/figma-paperclip/src/graph.ts ================================================ import * as chalk from "chalk"; import * as path from "path"; import { FigmaApi } from "./api"; import * as fsa from "fs-extra"; import { findLayer, httpGet, logError, logVerb, logWarn, md5 } from "./utils"; import * as https from "https"; import { DependencyGraph, DependencyKind, DesignDependency, DesignFileFontImport, DesignFileImport, DesignFileImportKind, ExportSettings, flattenNodes, ExcludeRule, FRAME_EXPORT_SETTINGS, getNodeById, getNodeExportFileName, getNodeParent, isExported, NodeType, getUniqueNodeName, shouldExport, getNodePage } from "./state"; import { kebabCase, uniq } from "lodash"; import { DEFAULT_EXPORT_SETTINGS } from "./constants"; const LOAD_CHUNK_SIZE = 50; type LoadDependenciesOptions = { exclude: ExcludeRule[]; cwd: string; }; export const loadDependencies = async ( fileKeys: string[], api: FigmaApi, options: LoadDependenciesOptions ) => { let graph: DependencyGraph = {}; // for TESTING only! const cacheFile = path.join( options.cwd, `${md5(JSON.stringify(fileKeys))}.cache` ); if (fsa.existsSync(cacheFile) && !process.env.REFETCH) { graph = JSON.parse(fsa.readFileSync(cacheFile, "utf-8")); } else { // load all for (const fileKey of fileKeys) { await loadDesignFile(fileKey, api, options, graph); } if (process.env.CACHE || process.env.REFETCH) { fsa.writeFileSync(cacheFile, JSON.stringify(graph, null, 2)); } } return graph; }; const loadDesignFile = async ( fileKey: string, api: FigmaApi, options: LoadDependenciesOptions, graph: DependencyGraph = {} ): Promise => { if (graph[fileKey]) { return graph; } const imports: Record = {}; const dep = (graph[fileKey] = { kind: DependencyKind.Design, imports, name: null, fileKey, document: null, styles: null }); const file = await api.getFile(fileKey); Object.assign(dep, { name: file.name, document: file.document, styles: file.styles }); logVerb(`Loading design ${chalk.bold(file.name)}`); const fonts = await loadFonts(dep, graph); Object.assign(imports, fonts); // await loadFramePreviews(dep, graph, api); await loadMedia(dep, graph, api); const foreignComponentIds = Object.keys(file.components).filter(importId => { const node = getNodeById(importId, file.document); if (node && node.type == NodeType.Component) { return false; } return true; }); await Promise.all( foreignComponentIds.map(async importId => { let componentInfo: any; const componentRef = file.components[importId]; try { componentInfo = (await api.getComponent(componentRef.key)) as any; } catch (e) { if (e.status === 404) { logWarn(`Unable to find component ${chalk.bold(componentRef.name)}`); return; } return; } if (componentInfo.meta.file_key === fileKey) { return; } imports[importId] = { kind: DesignFileImportKind.Design, nodeId: componentInfo.meta.node_id, fileKey: componentInfo.meta.file_key }; await loadDesignFile(componentInfo.meta.file_key, api, options, graph); }) ); await Promise.all( Object.keys(file.styles).map(async id => { const style = file.styles[id]; try { const info = await api.getStyle(style.key); if (info.meta.file_key === fileKey) { return; } imports[id] = { kind: DesignFileImportKind.Design, nodeId: info.meta.node_id, fileKey: info.meta.file_key }; await loadDesignFile(info.meta.file_key, api, options, graph); } catch (e) { logWarn(`Can't load style info for ${chalk.bold(style.name)}`); // console.log(e); } }) ); return graph; }; const loadFonts = async (dep: DesignDependency, graph: DependencyGraph) => { const deps: Record = {}; const fonts = getDesignFonts(dep); await Promise.all( fonts.map(async font => { const fileKey = getFontKey(font); deps[font] = { kind: DesignFileImportKind.Font, fileKey }; if (graph[fileKey]) { return; } const dep = (graph[fileKey] = { kind: DependencyKind.Font, fileKey: fileKey, content: null }); try { logVerb(`Loading font ${chalk.bold(font)}`); let css = await httpGet({ host: `fonts.googleapis.com`, path: `/css?family=${encodeURIComponent( font )}:100,200,300,400,500,600,700` }); // PC doesn't support urls without " css = css.replace(/url\((.*?)\)/g, (_, url, ...args) => { return `url("${url}")`; }); dep.content = ""; } catch (e) { logWarn( `Could not download ${chalk.bold( font )} font, you'll need to do it manually.` ); } }) ); return deps; }; const loadMedia = async ( dep: DesignDependency, graph: DependencyGraph, api: FigmaApi ) => { let nodeIdsByExport: Record< string, { settings: ExportSettings; nodes: Record; } > = {}; let mediaCount = 0; const walk = (node: any, each: any) => { if (node.type === "INSTANCE") { return; } each(node); if (node.children) { for (const child of node.children) { walk(child, each); } } }; walk(dep.document, child => { if (shouldExport(child)) { const exportSettings = ((child as any).exportSettings?.length && (child as any).exportSettings) || [ DEFAULT_EXPORT_SETTINGS, FRAME_EXPORT_SETTINGS ]; for (const setting of exportSettings) { if (setting.format === "PDF") { logWarn(`Cannot download PDF for layer: "${child.name}"`); continue; } if (setting.constraint.type !== "SCALE") { logWarn( `Cannot download "${child.name}" export since it doesn't have SCALE constraint.` ); continue; } nodeIdsByExport = addNodeToDownload(child, nodeIdsByExport, setting); mediaCount++; } } }); if (mediaCount === 0) { return; } logVerb(`Loading media for ${chalk.bold(dep.name)} (${mediaCount} assets)`); await Promise.all( Object.keys(nodeIdsByExport).map(async key => { const { settings, nodes } = nodeIdsByExport[key]; await loadImages(nodes, settings, graph, dep, api); }) ); }; const loadImages = async ( nodes, settings: ExportSettings, graph: DependencyGraph, dep: DesignDependency, api: FigmaApi ) => { const nodeIds = Object.keys(nodes); // Need to chunk these assets, otherwise we may hug Figma to death. // let prog = 0; const promises = []; for (let i = 0, { length } = nodeIds; i < length; i += LOAD_CHUNK_SIZE) { const chunkIds = nodeIds.slice(i, i + LOAD_CHUNK_SIZE); // prog += chunkIds.length; // console.log((prog / length) * 100); promises.push( (async () => { try { const result = await api.getImage(dep.fileKey, { ids: chunkIds.join(","), format: settings.format.toLowerCase() as any, scale: settings.constraint.value }); for (const nodeId in result.images) { const url = result.images[nodeId]; const node = getNodeById(nodeId, dep.document); if (!url) { logError( `Could not fetch asset for ${chalk.bold( String(dep.name) + " / " + getNodePage(nodeId, dep.document).name + " / " + node.name )}` ); continue; } const fileKey = getNodeExportFileName( nodes[nodeId] as any, dep.document, settings ); graph[fileKey] = { nodeId, settings, fileKey, kind: DependencyKind.Media, url: result.images[nodeId] }; } } catch (e) { logError(`Can't fetch assets: ${chunkIds.join(", ")}`); logError(JSON.stringify(e)); } })() ); } await Promise.all(promises); }; const loadFramePreviews = async ( dep: DesignDependency, graph: DependencyGraph, api: FigmaApi ) => { const frames = {}; logVerb(`Loading previews for ${chalk.bold(dep.name)}`); for (const canvas of dep.document.children) { if (canvas.type === NodeType.Canvas) { for (const frame of canvas.children) { if ((frame as any).visible !== false) { frames[frame.id] = frame; } } } } await loadImages(frames, FRAME_EXPORT_SETTINGS, graph, dep, api); }; const getFontKey = font => kebabCase(font); const getDesignFonts = (dep: DesignDependency) => { let fonts: string[] = []; findLayer(dep.document, descendent => { if (descendent.type === "TEXT") { fonts.push(descendent.style.fontFamily); } return false; }); fonts = uniq(fonts); return fonts; }; const addNodeToDownload = (child, rec, setting: any): any => { const key = getSettingKey(setting); if (!rec[key]) { rec[key] = { settings: setting, nodes: {} }; } rec[key].nodes[child.id] = child; return rec; }; const getSettingKey = setting => setting.format + setting.constraint.type + setting.constraint.value; ================================================ FILE: packages/figma-paperclip/src/index.ts ================================================ export * from "./init"; export * from "./pull"; ================================================ FILE: packages/figma-paperclip/src/init.ts ================================================ import { Config } from "./state"; import * as inquirer from "inquirer"; import * as chalk from "chalk"; import * as fsa from "fs-extra"; import { merge } from "lodash"; import { CONFIG_FILE_NAME, getConfigPath, logInfo, COMMAND_NAME } from "./utils"; export type InitOptions = { cwd: string; }; const DEFAULT_CONFIG: Config = { sources: [], outputDir: null, atoms: { prefix: "$" } }; export const init = async ({ cwd }: InitOptions) => { const { url, outputDir } = await inquirer.prompt([ { type: "input", name: "url", message: "What's the Figma url that you'd like to import?" }, { type: "input", name: "outputDir", default: "src", message: "Where would you like your designs to be saved to?" } ]); const config: Config = merge({}, DEFAULT_CONFIG, { sources: [url], outputDir }); const source = `module.exports = ${JSON.stringify(config, null, 2)};`; logInfo( `Wrote ${CONFIG_FILE_NAME}, you can now run ${chalk.bold( `${COMMAND_NAME} pull` )}.` ); fsa.writeFileSync(getConfigPath(cwd), source); }; ================================================ FILE: packages/figma-paperclip/src/memo.ts ================================================ import * as LRU from "lru-cache"; const DEFAULT_LRU_MAX = 1000 * 100; // need this for default arguments const getArgumentCount = (fn: Function) => { const str = fn.toString(); const params = str.match(/\(.*?\)|\w+\s*\=\>/)[0]; const args = params .replace(/[=>()]/g, "") .split(/\s*,\s*/) .filter(arg => arg.substr(0, 3) !== "..."); return args.length; }; export const memoize = any>( fn: TFunc, lruMax: number = DEFAULT_LRU_MAX, argumentCount: number = getArgumentCount(fn) ) => { if (argumentCount == Infinity || isNaN(argumentCount)) { throw new Error(`Argument count cannot be Infinity, 0, or NaN.`); } if (!argumentCount) { console.error(`Argument count should not be 0. Defaulting to 1.`); argumentCount = 1; } return compilFastMemoFn(argumentCount, lruMax > 0)( fn, new LRU({ max: lruMax }) ) as TFunc; }; export const shallowEquals = (a, b) => { const toa = typeof a; const tob = typeof b; if (toa !== tob) { return false; } if (toa !== "object" || !a || !b) { return a === b; } if (Object.keys(a).length !== Object.keys(b).length) { return false; } for (const key in a) { if (a[key] !== b[key]) { return false; } } return true; }; export const reuser = ( lruMax: number = DEFAULT_LRU_MAX, getKey: (value: TValue) => string, equals: (a: TValue, b: TValue) => boolean = shallowEquals ): ((value: TValue) => TValue) => { const cache = new LRU({ max: lruMax }); return (value: TValue) => { const key = getKey(value); if (!cache.has(key) || !equals(cache.get(key), value)) { cache.set(key, value); } return cache.get(key); }; }; let _memoFns = {}; const compilFastMemoFn = (argumentCount: number, acceptPrimitives: boolean) => { const hash = "" + argumentCount + acceptPrimitives; if (_memoFns[hash]) { return _memoFns[hash]; } const args = Array.from({ length: argumentCount }).map((v, i) => `arg${i}`); let buffer = ` return function(fn, keyMemo) { var memo = new WeakMap(); return function(${args.join(", ")}) { var currMemo = memo, prevMemo, key; `; for (let i = 0, n = args.length - 1; i < n; i++) { const arg = args[i]; buffer += ` prevMemo = currMemo; key = ${arg}; ${ acceptPrimitives ? `if ((typeof key !== "object" || !key) && !(key = keyMemo.get(${arg}))) { keyMemo.set(${arg}, key = {}); }` : "" } if (!(currMemo = currMemo.get(key))) { prevMemo.set(key, currMemo = new WeakMap()); } `; } const lastArg = args[args.length - 1]; buffer += ` key = ${lastArg}; ${ acceptPrimitives ? ` if ((typeof key !== "object" || !key) && !(key = keyMemo.get(${lastArg}))) { keyMemo.set(${lastArg}, key = {}); }` : "" } if (!currMemo.has(key)) { try { currMemo.set(key, fn(${args.join(", ")})); } catch(e) { throw e; } } return currMemo.get(key); }; }; `; return (_memoFns[hash] = new Function(buffer)()); }; ================================================ FILE: packages/figma-paperclip/src/pull.ts ================================================ import { COMMAND_NAME, configFileExists, logError, extractSourceUrlInfo, readConfig, SourceUrlInfo, logInfo, logVerb, logWarn } from "./utils"; import * as chalk from "chalk"; import * as path from "path"; import * as fsa from "fs-extra"; import * as https from "https"; import * as plimit from "p-limit"; import { FigmaApi } from "./api"; import { loadDependencies } from "./graph"; import { translateFigmaGraph } from "./translate"; import { DependencyGraph, DependencyKind, OutputFile, OutputFileKind } from "./state"; import { translateFigmaGraph2 } from "./translate2"; export type PullOptions = { cwd: string; token: string; }; export const pull = async ({ cwd, token }: PullOptions) => { if (!configFileExists(cwd)) { return logError( `Config not found, try running ${chalk.bold( `${COMMAND_NAME} init` )} first!` ); } const config = readConfig(cwd); const api = new FigmaApi(token); const sourceInfo = config.sources.map(extractSourceUrlInfo); const fileKeys = await getFileKeys(sourceInfo, api); logInfo(chalk.bold(`Loading dependency graph 🌎`)); const graph: DependencyGraph = await loadDependencies(fileKeys, api, { cwd, exclude: config.exclude || [] }); logInfo(chalk.bold(`Building Paperclip files 🔨`)); // 2. translate graph into files const files = translateFigmaGraph2(graph, { includes: config.inject || [], exclude: config.exclude || [], config }); logInfo(chalk.bold(`Writing files 💾`)); // 3. write files await writeFiles(files, cwd, config.outputDir); console.log("Done! 🎉"); process.exit(); }; const writeFiles = async ( files: OutputFile[], cwd: string, outputDir: string ) => { const promises = []; const limit = plimit(20); for (const file of files) { const absoluePath = path.join(cwd, outputDir, file.relativePath); fsa.mkdirpSync(path.dirname(absoluePath)); if (file.kind === OutputFileKind.Buffer) { logVerb(`Write ${absoluePath.replace(cwd + "/", "")}`); fsa.writeFileSync(absoluePath, file.content); } else if (file.kind === OutputFileKind.Remote) { promises.push(downloadFile(file.url, absoluePath, cwd, limit)); } } await Promise.all(promises); }; const downloadFile = async ( url: string, absoluePath: string, cwd: string, limit: any ) => { if (fsa.existsSync(absoluePath) && fsa.lstatSync(absoluePath).size) { return; } const relativePath = chalk.bold(absoluePath.replace(cwd + "/", "")); return limit(() => { return new Promise((resolve, reject) => { logVerb(`Download ${url} -> ${relativePath}`); const onError = () => { logError(`Unable to download ${url}`); ws.close(); reject(); }; const ws = fsa.createWriteStream(absoluePath); try { https .get(url, response => { if (response.statusCode !== 200) { logWarn(`Could not download asset ${chalk.bold(relativePath)}`); return reject(); } ws.on("finish", function() { resolve(null); }); response.pipe(ws, { end: true }); response.on("error", onError); }) .on("error", onError); } catch (e) { onError(); } }); }); }; const getFileKeys = async ( info: SourceUrlInfo[], api: FigmaApi ): Promise => { const fileKeys = []; for (const part of info) { if (part.fileKey) { fileKeys.push(part.fileKey); } else if (part.projectId) { const projectFiles = await api.getProjectFiles(part.projectId); for (const projectFile of projectFiles) { fileKeys.push(projectFile.fileKey); } } } return fileKeys; }; ================================================ FILE: packages/figma-paperclip/src/state.ts ================================================ import * as fs from "fs"; import * as chalk from "chalk"; import * as path from "path"; import { CONFIG_FILE_NAME } from "./constants"; import { memoize } from "./memo"; import { camelCase, kebabCase, pick } from "lodash"; import { logError, pascalCase } from "./utils"; import { getLayerStyle } from "./translate/context"; export type AtomsConfig = { globalVars?: boolean; typePrefix?: boolean; prefix?: string; }; export type Config = { // project urls sources: string[]; namespace?: string; // pc files to include inject?: string[]; exclude?: ExcludeRule[]; atoms?: AtomsConfig; outputDir: string; }; export type ExcludeRule = { pattern: RegExp; }; // _7113_testB3_testA24 export const cleanLabel = (name: string) => { // remove emojis return name.replace(/[^a-zA-Z0-9\-\_\s]/g, ""); }; const MAX_LABEL_NAME_LENGTH = 40; export type FileConfig = { key: string; version?: string; }; export type CompilerOptions = { componentsOnly?: boolean; includeAbsoluteLayout?: boolean; includePreviews?: boolean; }; export enum DesignFileImportKind { Font = "Font", Design = "Design" } export type BaseDesignFileImport = { kind: TKind; fileKey: string; }; export type DesignFileFontImport = BaseDesignFileImport< DesignFileImportKind.Font >; export type DesignFileDesignImport = BaseDesignFileImport< DesignFileImportKind.Design > & { nodeId: string; }; export type DesignFileImport = DesignFileDesignImport | DesignFileFontImport; export type Point = { x: number; y: number }; export enum DependencyKind { Font, Design, Media } export type BaseDependency = { kind: TKind; fileKey: string; }; export type DesignDependency = { name: string; fileKey: string; imports: Record; document: Document; styles: any; } & BaseDependency; export type FontDependency = { content: string; } & BaseDependency; export type MediaDependency = { url: string; nodeId: any; settings: ExportSettings; } & BaseDependency; export type Dependency = FontDependency | DesignDependency | MediaDependency; export type DependencyGraph = Record; export enum OutputFileKind { Buffer, Remote } export type BaseOutputFile = { relativePath: string; kind: TKind; }; export type BufferOutputFile = { content: string; } & BaseOutputFile; export type RemoteOutputFile = { url: string; } & BaseOutputFile; export type OutputFile = BufferOutputFile | RemoteOutputFile; export type Project = { id: number; name: string; files: ProjectFile[]; }; export type ProjectFile = { key: string; name: string; }; export enum FileNameFormat { Preserve = "preserve", CamelCase = "camel-case", PascalCase = "pascal-case", SnakeCase = "snake-case", KebabCase = "kebab-case" } // based on https://www.figma.com/developers/api export enum NodeType { Document = "DOCUMENT", Rectangle = "RECTANGLE", Ellipse = "ELLIPSE", Star = "STAR", REGULAR_POLYGON = "REGULAR_POLYGON", Line = "LINE", Canvas = "CANVAS", Group = "GROUP", Frame = "FRAME", Vector = "VECTOR", Instance = "INSTANCE", Component = "COMPONENT", ComponentSet = "COMPONENT_SET", Text = "TEXT" } export type BaseNode = { id: string; name: string; type: TType; }; export type Color = { r: number; g: number; b: number; a: number; }; export type Paint = { blendMode: string; type: string; color: Color; }; export type LayoutConstraint = {}; export type EasingType = {}; export type Rectangle = { x: number; y: number; width: number; height: number; }; export type Effect = any; export type Vector = { x: number; y: number }; export type Transform = [number, number, number][]; export type Path = { path: string; }; export type StyleType = "FILL" | "TEXT" | "EFFECT" | "GRID"; export type Constraint = { type: "SCALE" | "WIDTH" | "HEIGHT"; value: number; }; export type ExportSettings = { suffix: string; format: string; constraint: Constraint; }; export const FRAME_EXPORT_SETTINGS: ExportSettings = { suffix: null, format: "png", constraint: { type: "SCALE", value: 3 } }; export const DEFAULT_SHAPE_EXPORT_SETTINGS: ExportSettings = { suffix: null, format: "svg", constraint: { type: "SCALE", value: 1 } }; export enum FillType { SOLID = "SOLID", GRADIENT_LINEAR = "GRADIENT_LINEAR", GRADIENT_RADIAL = "GRADIENT_RADIAL", GRADIENT_ANGULAR = "GRADIENT_ANGULAR", DIAMOND_GRADIENT = "DIAMOND_GRADIENT", IMAGE = "IMAGE" } type BaseFill = { visible?: boolean; blendMode: string; type: TType; }; export type SolidFill = { blendMode: string; color: Color; } & BaseFill; export type ImageFill = { blendMode: string; scaleMode: string; imageRef: string; } & BaseFill; export type GradientStop = { color: Color; position: number; }; export type LinearGradient = { gradientHandlePositions: Vector[]; gradientStops: GradientStop[]; } & BaseFill; export type RadialGradient = { gradientHandlePositions: Vector[]; gradientStops: GradientStop[]; } & BaseFill; export type AngularGradient = { gradientHandlePositions: Vector[]; gradientStops: GradientStop[]; } & BaseFill; export type DiamondGradient = { gradientHandlePositions: Vector[]; gradientStops: GradientStop[]; } & BaseFill; export type Fill = | SolidFill | LinearGradient | RadialGradient | AngularGradient | DiamondGradient | ImageFill; export type VectorNodeProps = { locked: boolean; blendMode: string; preserveRatio: boolean; exportSettings?: ExportSettings[]; layoutAlign: "MIN" | "CENTER" | "MAX" | "STRETCH"; constraints: LayoutConstraint; transitionNodeID?: string; transitionDuration?: number; transitionEasing?: EasingType; opacity: number; absoluteBoundingBox: Rectangle; effects: Effect[]; size: Vector; relativeTransform: Transform; isMask: boolean; fills: Fill[]; fillGeometry: Path[]; strokes: Fill[]; strokeWeight: number; strokeCap: string; strokeJoin: string; strokeDashes: number[]; strokeMiterAngle: number[]; strokeGeometry: Path[]; strokeAlign: string; styles: Record; }; export type Document = { children: Node[]; } & BaseNode; export type Canvas = { backgroundColor: Color; exportSettings: ExportSettings[]; children: Node[]; } & BaseNode; export type Text = { characters: string; style: Record; } & VectorNodeProps & BaseNode; export type GroupNode = { children: Node[]; } & VectorNodeProps & BaseNode; export type VectorNode = {} & VectorNodeProps & BaseNode; export type RectangleNode = { cornerRadius: number; rectangleCornerRadii: number[]; } & VectorNodeProps & BaseNode; export type EllipseNode = {} & VectorNodeProps & BaseNode; export type StarNode = {} & VectorNodeProps & BaseNode; export type RegularPolygonNode = {} & VectorNodeProps & BaseNode; export type LineNode = {} & VectorNodeProps & BaseNode; export type FrameProps = { children: Node[]; clipsContent: boolean; } & VectorNodeProps; export type Frame = FrameProps & BaseNode; export type Instance = { componentId: string; } & FrameProps & BaseNode; export type ComponentSet = {} & FrameProps & BaseNode; export type Component = {} & FrameProps & BaseNode; export type VectorLikeNode = VectorNode | EllipseNode | StarNode; export type Exportable = Frame | VectorNode | RectangleNode; export type Parent = Frame | GroupNode | Document | Canvas | Instance; export type Node = | Document | Canvas | GroupNode | Frame | VectorNode | RectangleNode | RegularPolygonNode | StarNode | EllipseNode | LineNode | Instance | Text | Component | ComponentSet; export const hasVectorProps = (node: Node): node is VectorLikeNode => { return ( node.type === NodeType.Frame || node.type == NodeType.Rectangle || node.type == NodeType.Vector || node.type == NodeType.Instance || node.type == NodeType.Component ); }; export const isExported = (node: Node): node is Exportable => { return ( node?.type !== NodeType.Document && node?.exportSettings?.length > 0 && (node as any).visible !== false ); }; export const shouldExport = (node: Node) => { return ( (node as any).visible !== false && (isExported(node) || node.type === "COMPONENT") ); }; export const isVectorLike = (node: Node): node is VectorLikeNode => { return ( node.type === NodeType.Vector || node.type == NodeType.Star || node.type == NodeType.Ellipse ); }; export const readConfigSync = (cwd: string) => JSON.parse(fs.readFileSync(path.join(cwd, CONFIG_FILE_NAME), "utf8")); export const flattenNodes = memoize((node: Node): Node[] => { const treeNodeMap = getTreeNodeIdMap(node); return Object.values(treeNodeMap) as Node[]; }); export const getNodeById = memoize( (nodeId: string, document: Document): Node => { const d = Date.now(); const map = getTreeNodeIdMap(document); return map[nodeId]; } ); export const containsNode = memoize((node: Node, parent: Node): boolean => { return flattenNodes(parent).indexOf(node) !== -1; }); export const getAllTextNodes = memoize((parent: Node): Text[] => { return flattenNodes(parent).filter( node => node.type === NodeType.Text ) as Text[]; }) as (parent: Node) => Text[]; export const hasChildren = (node: Node): node is Parent => { return (node as any).children?.length > 0; }; export const getCleanedName = (name: string) => { // remove certain chars let newName = kebabCase( name.replace(/[\\/\-\s]/g, "_").replace(/[^_\w\d]/g, "") ); newName = exceedsMaxLabelName(newName) ? newName.substr(0, MAX_LABEL_NAME_LENGTH) : newName; return kebabCase(newName); }; const exceedsMaxLabelName = (name: string) => name.length > MAX_LABEL_NAME_LENGTH; export const getNodeExportFileName = ( node: Node, document: Document, settings: ExportSettings ) => { if (!containsNode(node, document)) { throw new Error(`document doesn't contain node`); } // want to include document name to make sure that there's no clashing between assets return `${getCleanedName(node.name)}@${ settings.constraint.value }.${settings.format.toLowerCase()}`; }; export const getOriginalNode = (nodeId: string, document: Document) => { return getNodeById( nodeId .split(";") .pop() .replace("I", ""), document ); }; const flattenComponentNodes = memoize((document: Document) => { const allChildren = []; for (const component of getAllComponents(document)) { allChildren.push(...flattenNodes(component)); } return allChildren; }); // export const getUniqueNodeName = (node: Node, document: Document) => { // Note that it's tempting to use a prettier name for assets, such as the _actual_ label, but we // don't want to allow designers to accidentally change assets out from underneath us. Keeping the ID within the // label itself is a bit more defensive. return getCleanedName(node.name) + "-" + kebabCase(node.id); }; export const getNodePath = memoize((node: Node, root: Node) => { const childParentMap = getChildParentMap(root); const idMap = getTreeNodeIdMap(root); let current = idMap[node.id]; const path: number[] = []; while (1) { const parent = childParentMap[current.id]; if (!parent) break; const i = (parent as any).children.indexOf(current); path.unshift(i); current = parent; } return path; }); export const getNodeByPath = (path: number[], root: any) => { let curr = root; for (let i = 0, { length } = path; i < length; i++) { curr = curr.children[path[i]]; if (!curr || !curr.children) { break; } } return curr; }; export const getNodePage = (nodeId: string, document: Document) => { for (const page of document.children) { if (containsNode(getNodeById(nodeId, document), page)) { return page; } } return null; }; export const getOwnerComponent = (node: Node, document: Document) => { if (node.type === NodeType.Component) { return null; } const ancestors = getNodeAncestors(node, document); for (const ancestor of ancestors) { if (ancestor.type === NodeType.Component) { return ancestor; } } return null; }; export const getOwnerInstance = (node: Node, document: Document) => { if (node.type === NodeType.Instance) { return null; } const ancestors = getNodeAncestors(node, document); for (const ancestor of ancestors) { if (ancestor.type === NodeType.Instance) { return ancestor; } } return null; }; export const getMixin = memoize( (mixinId: string, dep: DesignDependency, graph: DependencyGraph) => { let mixin; const imp = dep.imports[mixinId] as DesignFileDesignImport; if (imp) { mixin = (graph[imp.fileKey] as DesignDependency).styles[imp.nodeId]; } else { mixin = dep.styles[mixinId]; } return mixin; } ); export const getInstanceComponent = ( node: any, fileKey: string, graph: DependencyGraph ) => { const dep = graph[fileKey]; if (dep.kind !== DependencyKind.Design) { return null; } if (node.type === "COMPONENT_SET") { return node; } if (node.type === "COMPONENT") { // const parent = getNodeParent(node, dep.document); // if (parent.type === "COMPONENT_SET") { // return parent; // } return node; } let component; let componentDep; const imp = dep.imports[node.componentId] as any; if (imp) { componentDep = graph[imp.fileKey] as DesignDependency; component = getNodeById(imp.nodeId, componentDep.document); } else { componentDep = dep; component = getNodeById(node.componentId, dep.document); } if (component) { const parent = getNodeParent(component, componentDep.document); // if (parent.type === "COMPONENT_SET") { // return parent; // } else { return component; // } } // TODO - look for instance }; export const getComponentName = (componentOrSet: any) => { return pascalCase(componentOrSet.name); }; export const cleanupNodeId = (nodeId: string) => nodeId.replace(/[:;]/g, ""); export const getNodeAncestors = (node: Node, document: Document): Node[] => { return filterTreeNodeParents(node, document, () => true); }; export const getNodeFrame = (node: Node, document: Document): Frame => { const nodePath = getNodePath(node, document); let current: Parent = document; return getNodeByPath(nodePath.slice(0, 2), document); }; export const getNodeDependency = memoize( (node: Node, graph: DependencyGraph) => { for (const fileKey in graph) { const dep = graph[fileKey]; if ( dep.kind === DependencyKind.Design && containsNode(node, dep.document) ) { return dep; } } } ); export const getNodeDependencyById = ( nodeId: string, graph: DependencyGraph ): DesignDependency => { for (const fileKey in graph) { const dep = graph[fileKey]; if (dep.kind !== DependencyKind.Design) { continue; } const node = getNodeById(nodeId, dep.document); if (node) { return dep; } } }; export const filterTreeNodeParents = ( node: Node, root: Node, filter: (node: Node) => boolean ) => { const parents: Node[] = []; const path = getNodePath(node, root); if (!path.length) return null; for (let i = path.length; i--; ) { const parent = getNodeByPath(path.slice(0, i), root); if (filter(parent)) { parents.push(parent); } } return parents; }; export const getChildParentMap = memoize( (current: Node): Record => { const idMap = getTreeNodeIdMap(current); const parentChildMap: any = {}; for (const id in idMap) { const parent = idMap[id]; if ((parent as any).children) { for (const child of (parent as any).children) { parentChildMap[child.id] = parent; } } } return parentChildMap; } ); export const getTreeNodeIdMap = memoize( (current: Node): Record => { const map = { [current.id]: current }; if ((current as any).children) { Object.assign(map, ...(current as any).children.map(getTreeNodeIdMap)); } return map; } ); export const getAllComponents = memoize((node: any) => { return flattenNodes(node).filter( node => node.type === NodeType.Component ) as Component[]; }); export const getAllComponentSets = memoize((document: Document) => { return flattenNodes(document).filter( node => node.type === NodeType.ComponentSet ) as ComponentSet[]; }); export const getNodeParent = memoize((node: Node, document: Document) => { return getChildParentMap(document)[node.id]; }); export const getMixinStyles = memoize( (mixinId: string, fileKey: string, graph: DependencyGraph) => { const dep = graph[fileKey]; if (dep.kind !== DependencyKind.Design) { return null; } const imp = dep.imports[mixinId] as DesignFileDesignImport; const mixinDep = graph[imp?.fileKey || fileKey] as DesignDependency; const mixinNode = getNodeById(imp?.nodeId || mixinId, mixinDep.document); } ); export const extractMixedInStyles = memoize( (layer: any): Record => { const style = getLayerStyle(layer); const ret = {}; for (const type in layer.styles) { const mixinId = layer.styles[type]; if (type === "stroke" || type === "strokes") { ret[mixinId] = pick(style, "borderColor"); } else if (type === "fill" || type === "fills") { ret[mixinId] = pick(style, "color", "background"); } else if (type === "effect") { ret[mixinId] = pick(style, "boxShadow"); } else if (type === "text") { ret[mixinId] = pick( style, "fontFamily", "letterSpacing", "lineHeight", "fontWeight", "fontSize", "textAlign" ); } } return ret; } ); export const getImports = memoize( (entry: Dependency, graph: DependencyGraph) => { const deps: Dependency[] = []; if (entry.kind === DependencyKind.Design) { for (const refId in entry.imports) { const imp = entry.imports[refId]; deps.push(graph[imp.fileKey]); } } return deps; } ); ================================================ FILE: packages/figma-paperclip/src/translate/context.ts ================================================ import { uniq } from "lodash"; import { Config, DependencyGraph, ExcludeRule, OutputFile, OutputFileKind, Point } from "../state"; import { logWarn } from "../utils"; import * as chalk from "chalk"; import { memoize } from "../memo"; export const BLEND_MODE_MAP = { NORMAL: "normal", DARKEN: "darken", MULTIPLY: "multiply", COLOR_BURN: "color-burn", LIGHTEN: "lighten", SCREEN: "screen", COLOR_DODGE: "color-dodge", OVERLAY: "overlay", SOFT_LIGHT: "soft-light", HARD_LIGHT: "hard-light", DIFFERENCE: "difference", EXCLUSION: "exclusion", HUE: "hue", LUMINOSITY: "luminosity", SATURATION: "saturation", COLOR: "color" }; export type TranslateOptions = { includes: string[]; exclude: ExcludeRule[]; config: Config; }; export type TranslateContext = { content: string; options: TranslateOptions; graph: DependencyGraph; lineNumber: number; isNewLine: boolean; logs: string[]; indent: string; fileKey: string; mixins: Record; isFrame: boolean; framePosition?: Point; }; export type TranslateContext2 = { content: string; options: TranslateOptions; graph: DependencyGraph; lineNumber: number; currentRelativeFilePath?: string; isNewLine: boolean; indent: string; logs: string[]; currentFileKey?: string; currentPageName?: string; mixins: Record; isFrame: boolean; framePosition?: Point; files: OutputFile[]; }; export const ontext = ( fileKey: string, graph: DependencyGraph, options: TranslateOptions ): TranslateContext => ({ content: "", lineNumber: 0, fileKey, logs: [], graph, options, mixins: {}, isNewLine: false, indent: " ", isFrame: false }); export const startFile = ( currentRelativeFilePath: string, currentFileKey: string, context: TranslateContext2 ) => ({ ...context, currentFileKey, currentRelativeFilePath }); export const startPageFile = ( currentRelativeFilePath: string, currentFileKey: string, currentPageName: string, context: TranslateContext2 ): TranslateContext2 => ({ ...startFile(currentRelativeFilePath, currentFileKey, context), currentPageName }); export const addBuffer = (buffer: string, context: TranslateContext2) => ({ ...context, content: context.content + (context.isNewLine ? context.indent.repeat(context.lineNumber) : "") + buffer, isNewLine: buffer.lastIndexOf("\n") === buffer.length - 1 }); export const startBlock = (context: TranslateContext2) => ({ ...context, lineNumber: context.lineNumber + 1 }); export const endBlock = (context: TranslateContext2) => ({ ...context, lineNumber: context.lineNumber - 1 }); export const createContext = ( fileKey: string, graph: DependencyGraph, options: TranslateOptions ): TranslateContext => ({ content: "", lineNumber: 0, fileKey, graph, logs: [], options, mixins: {}, isNewLine: false, indent: " ", isFrame: false }); export const createContext2 = ( graph: DependencyGraph, options: TranslateOptions ): TranslateContext2 => ({ content: "", lineNumber: 0, logs: [], graph, files: [], options, mixins: {}, isNewLine: false, indent: " ", isFrame: false }); export const addFile = (context: TranslateContext2): TranslateContext2 => { return { ...context, content: "", lineNumber: 0, isNewLine: false, isFrame: false, currentRelativeFilePath: null, currentPageName: null, currentFileKey: null, files: [ ...context.files, { kind: OutputFileKind.Buffer, relativePath: context.currentRelativeFilePath, content: context.content } ] }; }; export const addRemoteFile = ( url: string, context: TranslateContext2 ): TranslateContext2 => { return { ...context, content: "", lineNumber: 0, isNewLine: false, isFrame: false, currentRelativeFilePath: null, currentPageName: null, currentFileKey: null, files: [ ...context.files, { kind: OutputFileKind.Remote, relativePath: context.currentRelativeFilePath, url } ] }; }; export const px = (value: number) => round(value, 2) + "px"; export const round = (value: number, dec: number) => parseFloat(value.toFixed(dec)); // TODO - need to create util for plucking out styles that are part of atoms export const getLayerStyle = memoize((layer: any) => { const style: any = {}; if (layer.type !== "TEXT") { if (layer.fills) { const value = getFillStyleValue(layer, layer.fills); if (value) { style.background = value; const containsBlendModes = layer.fills.some(fill => { return fill.blendMode !== "NORMAL"; }); if (containsBlendModes) { style.backgroundBlendMode = layer.fills .map(fill => { return BLEND_MODE_MAP[fill.blendMode]; }) .join(", "); } } } if (layer.effects) { const shadows = layer.effects.filter( effect => (effect.type === "DROP_SHADOW" || effect.type === "INNER_SHADOW") && effect.visible ); if (shadows.length) { style.boxShadow = shadows .map(shadow => { const buffer = []; if (shadow.type === "INNER_SHADOW") { buffer.push("inset"); } buffer.push( px(shadow.offset.x), px(shadow.offset.y), px(shadow.radius), getCSSRGBAColor(shadow.color) ); return buffer.join(" "); }) .join(", "); } } if (layer.rectangleCornerRadii) { if (uniq(layer.rectangleCornerRadii).length === 1) { style.borderRadius = px(layer.rectangleCornerRadii[0]); } else { style.borderTopLeftRadius = layer.rectangleCornerRadii[0] + "px"; style.borderTopRightRadius = layer.rectangleCornerRadii[1] + "px"; style.borderBottomLeftRadius = layer.rectangleCornerRadii[2] + "px"; style.borderBottomRightRadius = layer.rectangleCornerRadii[3] + "px"; } } } if (layer.type === "TEXT") { const solidFill = layer.fills.find(fill => fill.type === "SOLID"); if (solidFill) { style.color = getCSSRGBAColor(solidFill.color); } style.lineHeight = px(layer.style.lineHeightPx); style.fontFamily = layer.style.fontFamily + ", sans-serif"; style.fontWeight = layer.style.fontWeight; style.fontSize = px(layer.style.fontSize); style.textAlign = layer.style.textAlignHorizontal.toLowerCase(); style.letterSpacing = round(layer.style.letterSpacing, 2); } if (layer.strokes?.length) { const [stroke] = layer.strokes; if (stroke.color) { style.borderColor = getCSSRGBAColor(stroke.color); style.borderSyyle = stroke.type.toLowerCase(); style.borderWidth = layer.strokeWeight; } } return style; }); const getFillStyleValue = (layer: any, fills: any[]) => fills .reverse() .filter(fill => fill.visible !== false) .map((fill, index) => { switch (fill.type) { case "SOLID": { return getCSSRGBAColor(fill.color, index === fills.length - 1); } case "GRADIENT_LINEAR": { return getCSSLinearGradient(fill); } case "GRADIENT_RADIAL": { return getCSSRadialGradient(fill); } case "IMAGE": { return getCSSImageBackground(fill); } default: { // TODO - all gradient fills should work logLayerWarn(layer, `Cannot translate ${fill.type} fill to CSS`); return null; } } }) .filter(Boolean) .join(", "); const getCSSRGBAColor = ({ r, g, b, a }: any, last: boolean = true) => { const r2 = Math.round(r * 255); const g2 = Math.round(g * 255); const b2 = Math.round(b * 255); // TODO - generate hash let color; if (a !== 1) { color = `rgba(${r2}, ${g2}, ${b2}, ${round(a, 2)})`; } else { color = rgbToHex(r2, g2, b2); } return last ? color : `linear-gradient(0deg, ${color}, ${color})`; }; const getCSSImageBackground = ({ imageRef }: any) => { // TODO: https://github.com/crcn/figmark/issues/13 // TODO - need to get actual extension info. return `url("./${imageRef}.png")`; }; const getCSSRadialGradient = ({ gradientStops }: any) => { // TODO: https://github.com/crcn/figmark/issues/13 return `radial-gradient(${gradientStops .map(stop => { return `${getCSSRGBAColor(stop.color)} ${stop.position * 100}%`; }) .join(", ")})`; }; const rgbToHex = (r: number, g: number, b: number) => { return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); }; const logLayerWarn = (node: any, message: string) => { logWarn(`Layer ${chalk.bold(node.name)}: ${message}`); }; const getCSSLinearGradient = ({ gradientHandlePositions, gradientStops }: any) => { // TODO: https://github.com/crcn/figmark/issues/12 const radians = calcGradiantHandleRadians(gradientHandlePositions); return `linear-gradient(${radians}rad, ${gradientStops .map(stop => { return `${getCSSRGBAColor(stop.color)} ${stop.position * 100}%`; }) .join(", ")})`; }; const calcGradiantHandleRadians = ([first, second]: any) => { const ydiff = second.y - first.y; const xdiff = first.x - second.x; const radians = Math.atan2(-xdiff, -ydiff); return Number(radians.toFixed(3)); }; export const logContextWarn = (text: string, context: TranslateContext2) => { if (context.logs.includes(text)) { return context; } context = { ...context, logs: [...context.logs, text] }; logWarn(text); return context; }; ================================================ FILE: packages/figma-paperclip/src/translate/index.ts ================================================ import { DependencyGraph, DependencyKind, getNodeById, getNodeDependency, getNodeDependencyById, getNodeExportFileName } from "../state"; import { addBuffer, addFile, addRemoteFile, createContext2, startFile, TranslateContext2, TranslateOptions } from "./context"; import { getFontFile, getLayerMediaPath } from "./utils"; import { writeDesignModules } from "./modules"; import { writeDesignPages } from "./pages"; /* Generates design files with the following folder structure: atoms.pc figma/ atoms/ colors.pc typography.pc shadows.pc assets/ something.svg designs/ my-design-file-name/ page1.pc page2.pc components/ page.pc page2.pc onboarding-activation/ dev-ready-version-2.pc pseudocode: { let context = createContext2(graph, options); context = writeFonts(context); // previews are private and should not be directly referenced context = writeDesigns(context); context = writeMedia(context); return context.files; }; const writeDesigns = (context: TranslateContext2) => { for (const fileKey in context.graph) { const dep = context.graph[fileKey]; if (dep.kind === DependencyKind.Design) { context = writeDesignModules(dep, context); // context = writeDesignPages(dep, context); } } return context; }; const writeFonts = (context: TranslateContext2) => { for (const key in context.graph) { const dep = context.graph[key]; // content may be NULL if there is an error if (dep.kind !== DependencyKind.Font || !dep.content) { continue; } context = startFile(getFontFile(dep), dep.fileKey, context); context = addBuffer(dep.content, context); context = addFile(context); } return context; }; const writeMedia = (context: TranslateContext2) => { for (const key in context.graph) { const dep = context.graph[key]; if (dep.kind === DependencyKind.Media) { const nodeDep = getNodeDependencyById(dep.nodeId, context.graph); const node = getNodeById(dep.nodeId, nodeDep.document); context = startFile( getLayerMediaPath(node, nodeDep, dep.settings), key, context ); context = addRemoteFile(dep.url, context); } } return context; }; ================================================ FILE: packages/figma-paperclip/src/translate/modules.ts ================================================ import { Config, DependencyGraph, DependencyKind, DesignDependency, DesignFileDesignImport, extractMixedInStyles, flattenNodes } from "../state"; import { logWarn } from "../utils"; import { addBuffer, addFile, endBlock, startBlock, startFile, TranslateContext2 } from "./context"; import { getDesignModulesFile, getMixinValue, getMixinName, isStyleVar, writeElementBlock, writeStyleBlock, writeStyleDeclaration, writeStyleDeclarations } from "./utils"; import * as chalk from "chalk"; import { memoize } from "../memo"; export const writeDesignModules = ( dep: DesignDependency, context: TranslateContext2 ) => { context = startFile(getDesignModulesFile(dep), dep.fileKey, context); context = writeAtoms(dep, context); context = addFile(context); return context; }; const writeAtoms = (dep: DesignDependency, context: TranslateContext2) => { context = writeElementBlock( { tagName: "style" }, context => { context = writeMixinsBody(dep, context); return context; }, context ); return context; }; const writeMixinsBody = (dep: DesignDependency, context: TranslateContext2) => { const { vars, mixins } = getAtoms( dep, context.graph, context.options.config ) as any; context = writeStyleBlock( "@export", context => { context = writeVars(vars, context); context = writeMixins(mixins, context); return context; }, context ); return context; }; const writeVars = ( vars: Record, context: TranslateContext2 ) => { let selector = ":root"; if (context.options.config.atoms?.globalVars) { selector = `:global(${selector})`; } return writeStyleBlock( selector, context => { for (const name in vars) { context = writeStyleDeclaration(name, vars[name], context, false); } return context; }, context ); }; const writeMixins = ( mixins: Record>, context: TranslateContext2 ) => { for (const name in mixins) { context = writeStyleBlock( `@mixin ${name}`, context => { return writeStyleDeclarations(mixins[name], context); }, context ); context = writeStyleBlock( `.${name}`, context => { return writeStyleDeclarations(mixins[name], context); }, context ); } return context; }; export const getAtoms = memoize( (dep: DesignDependency, graph: DependencyGraph, config: Config) => { const vars = {}; const mixins = {}; for (const mixinId in dep.styles) { const mixin = dep.styles[mixinId]; const modelLayer = getGlobalMixinModelLayer(mixinId, dep.fileKey, graph); if (!modelLayer) { logWarn(`Could not find styles for "${chalk.bold(mixin.name)}"`); continue; } const style = extractMixedInStyles(modelLayer)[mixinId]; // should be grid if (!style) { continue; } const name = getMixinName(mixin, config); const value = getMixinValue(mixin, style); if (isStyleVar(mixin)) { vars[name] = value; } else { mixins[name] = value; } } return { vars, mixins }; } ); const getGlobalMixinModelLayer = ( mixinId: string, originFileKey: string, graph: DependencyGraph ) => { const origin = graph[originFileKey] as DesignDependency; const modelLayer = getDepMixinModelLayer(mixinId, origin); if (modelLayer) { return modelLayer; } // if not found, then need to look in other places where it is used. Note // that this may be problematic if the designer doesn't publish their designs. Maybe have linting if there's inconsistency? for (const filekKey in graph) { const dep = graph[filekKey]; if (dep.kind === DependencyKind.Design) { const impId = getDependentImportId(originFileKey, dep); if (impId) { const imp = dep.imports[impId] as DesignFileDesignImport; const depModelLayer = getDepMixinModelLayer(impId, dep); if (depModelLayer) { return depModelLayer; } } } } }; const getDepMixinModelLayer = (styleId: string, dep: DesignDependency) => { return flattenNodes(dep.document).find((layer: any) => { return layer.styles && Object.values(layer.styles).includes(styleId); }); }; const getDependentImportId = (importFileKey: string, dep: DesignDependency) => { for (const refId in dep.imports) { const imp = dep.imports[refId]; if (imp.fileKey === importFileKey) { return refId; } } return null; }; ================================================ FILE: packages/figma-paperclip/src/translate/pages.ts ================================================ import { add, camelCase, curry, omit, uniq } from "lodash"; import * as path from "path"; import * as chalk from "chalk"; import * as tc from "tinycolor2"; import { DependencyGraph, DependencyKind, DesignDependency, extractMixedInStyles, flattenNodes, FRAME_EXPORT_SETTINGS, DEFAULT_SHAPE_EXPORT_SETTINGS, getInstanceComponent, getMixin, getNodeDependency, getNodeExportFileName, getNodeFrame, getNodeParent, isExported, shouldExport, DesignFileDesignImport, getUniqueNodeName, getNodeById, getAllComponents } from "../state"; import { addBuffer, addFile, endBlock, getLayerStyle, logContextWarn, px, startBlock, startPageFile, TranslateContext2 } from "./context"; import { getDesignModulesFile, getDesignPageFile, getFontFile, getLayerMediaPath, getMixinName, getMixinValue, getStyleVarName, isStyleMixin, isStyleVar, writeElementBlock, writeStyleBlock, writeStyleDeclarations } from "./utils"; import { getAtoms } from "./modules"; import { memoize } from "../memo"; import { pascalCase } from "../utils"; export const writeDesignPages = ( dep: DesignDependency, context: TranslateContext2 ) => { for (const page of dep.document.children) { context = writePage(page, dep, context); } return context; }; const writePage = ( page: any, dep: DesignDependency, context: TranslateContext2 ) => { context = startPageFile( getDesignPageFile(page, dep), dep.fileKey, page.name, context ); // skip altogether if relative path is null if (context.currentRelativeFilePath == null) { return context; } context = writePageImports(dep, context); context = writeGenericStyles(context); context = writeGenericComponents(context); context = writePageFrames(page, context); context = writePageComponents(page, context); context = addFile(context); return context; }; const writeGenericStyles = (context: TranslateContext2) => { // style reset to make sure guides look good context = writeElementBlock( { tagName: "style" }, context => { context = writeStyleBlock( "*", context => { return writeStyleDeclarations( { fontFamily: `sans-serif` }, context ); }, context ); context = writeStyleBlock( "hr", context => { return writeStyleDeclarations( { opacity: 0.5 }, context ); }, context ); context = writeStyleBlock( ".asset-info", context => { context = writeStyleDeclarations( { display: "flex", flexDirection: "row", flexWrap: "wrap", gridColumnGap: `16px`, gridRowGap: `16px` }, context ); context = writeStyleBlock( ".asset-info-item", context => { // context = writeStyleBlock("&:hover", context => { // }, context); context = writeStyleBlock( "img", context => { context = writeStyleDeclarations( { maxWidth: `200px` }, context ); return context; }, context ); return context; }, context ); return context; }, context ); context = writeStyleBlock( ".frame-atom-info", context => { context = writeStyleDeclarations( { padding: "32px" }, context ); return context; }, context ); context = writeStyleBlock( ".color-blocks", context => { context = writeStyleDeclarations( { display: "flex", flexDirection: "row", flexWrap: "wrap", gridColumnGap: px(16), gridRowGap: px(16) }, context ); return context; }, context ); context = writeStyleBlock( ".color-block", context => { context = writeStyleDeclarations( { padding: px(20), fontWeight: 500 }, context ); return context; }, context ); return context; }, context ); return context; }; const writeGenericComponents = (context: TranslateContext2) => { return context; }; const writePageFrames = (page: any, context: TranslateContext2) => { for (const frame of page.children) { if (frame.type !== "COMPONENT") { context = writePageFrame(frame, context); } } return context; }; const writePageComponents = (page: any, context: TranslateContext2) => { const components = getAllComponents(page) as any[]; for (const frame of components) { if (frame.visible !== false) { context = writePageFrame(frame, context); } } return context; }; const writePageFrame = (frame: any, context: TranslateContext2) => { context = writeFrameComment(frame, context); context = writeFrameSummary(frame, context); return context; }; const writeFrameSummary = (frame: any, context: TranslateContext2) => { return writeFrameSummaryOuter(frame, context); }; const writeFrameSummaryOuter = (frame: any, context: TranslateContext2) => { return writeElementBlock( { tagName: "div", attributes: `export component as="${pascalCase( getUniqueNodeName( frame, (context.graph[context.currentFileKey] as DesignDependency).document ) )}"` }, context => { return writeFrameSummaryInner(frame, context); }, context ); }; const writeFrameSummaryInner = (frame: any, context: TranslateContext2) => { context = writeFramePreview(frame, context); context = writeFrameAtomInfo(frame, context); return context; }; const writeFramePreview = (frame: any, context: TranslateContext2) => { const framePreviewAssetPath = getLayerMediaPath( frame, context.graph[context.currentFileKey] as DesignDependency, FRAME_EXPORT_SETTINGS ); // Just want to display the frame preview and not the whole thing context = writeElementBlock( { tagName: "img", attributes: `src="${resolvePath(framePreviewAssetPath, context)}"` }, context => { context = writeElementBlock( { tagName: "style" }, context => { context = writeStyleDeclarations( { width: "100vw" }, context ); return context; }, context ); return context; }, context ); return context; }; const writeFrameAtomInfo = (frame: any, context: TranslateContext2) => { context = addBuffer(`{!hideAtoms? && `, context); context = startBlock(context); context = writeElementBlock( { tagName: "div", attributes: `className="frame-atom-info"` }, context => { return writeFrameAtomInfoInner(frame, context); }, context ); context = endBlock(context); context = addBuffer("}\n", context); return context; }; const writeFrameAtomInfoInner = (frame: any, context: TranslateContext2) => { const colors = getFrameMixins(frame, "FILL", context); const typography = getFrameMixins(frame, "TEXT", context); const assets = getFrameAssets( frame, context.graph[context.currentFileKey] as DesignDependency, context.graph ); // context = writeInfoSection( // `Component Instances`, // context => { // context = writeInstancePreviews(frame, context); // return context; // }, // context // ); if (Object.keys(colors).length) { context = writeInfoSection( `Colors`, context => { context = writeColorBlocks(colors, context); return context; }, context ); } if (Object.keys(typography).length) { context = writeInfoSection( `Typography`, context => { context = writeTypographyInfo(typography, context); return context; }, context ); } if (assets.length) { context = writeInfoSection( `Assets`, context => { context = writeAssetInfo(assets, context); return context; }, context ); } return context; }; const writeInstancePreviews = (frame: any, context: TranslateContext2) => { const dep = context.graph[context.currentFileKey] as DesignDependency; const instances = flattenNodes(frame).filter( layer => layer.type === "INSTANCE" ) as any; const used = {}; context = writeElementBlock( { tagName: "div", attributes: `className="asset-info"` }, context => { for (const instance of instances) { context = writeInstancePreview(instance, dep, used, context); } return context; }, context ); return context; }; const writeInstancePreview = ( instance: any, dep: DesignDependency, used: any, context: TranslateContext2 ) => { if (instance.visible === false) { return context; } const imp = dep.imports[instance.componentId] as DesignFileDesignImport; let impDep; let component; if (imp) { impDep = context.graph[imp.fileKey] as DesignDependency; component = getNodeById(imp.nodeId, impDep.document); } else { impDep = dep; component = getNodeById(instance.componentId, dep.document); } if (!component || used[component.id]) { return context; } used[component.id] = 1; const path = []; if (impDep !== dep) { path.push(getDependencyImportId(impDep)); } path.push(pascalCase(getUniqueNodeName(component, impDep))); if (component.visible === false) { return context; } // const exp = (component.exportSettings?.length && component.exportSettings || [FRAME_EXPORT_SETTINGS])[0]; // context = writeElementBlock( // { tagName: "div", attributes: `className="asset-info-item"` }, // context => { // context = addBuffer( // `\n`, // context // ); // return context; // }, // context // ); context = addBuffer(`<${path.join(".")} hideAtoms />`, context); return context; }; const getFrameAssets = ( frame: any, dep: DesignDependency, graph: DependencyGraph ) => { const exports = []; const walk = (node: any, dep: DesignDependency) => { if (node.type === "INSTANCE") { const component = getInstanceComponent(node, dep.fileKey, graph); if (component) { const componentDep = getNodeDependency(component, graph); walk(component, componentDep); } } if (isExported(node)) { exports.push(node); } if (node.children) { for (const child of node.children) { walk(child, dep); } } }; walk(frame, dep); return uniq( exports.map(exp => getLayerMediaPath( exp, getNodeDependency(exp, graph), isExported(exp) ? exp.exportSettings[0] : DEFAULT_SHAPE_EXPORT_SETTINGS ) ) ); }; const writeColorBlocks = ( colors: Record, context: TranslateContext2 ) => { context = writeElementBlock( { tagName: "div", attributes: `className="color-blocks"` }, context => { for (const name in colors) { context = writeColorBlock(name, colors[name], context); } return context; }, context ); return context; }; const writeTypographyInfo = ( typography: Record, context: TranslateContext2 ) => { for (const name in typography) { context = addBuffer(`
    .${name}
    \n`, context); } return context; }; const writeAssetInfo = (assets: string[], context: TranslateContext2) => { return writeElementBlock( { tagName: "div", attributes: `className="asset-info"` }, context => { for (const asset of assets) { context = writeAssetInfoItem(asset, context); } return context; }, context ); }; const writeAssetInfoItem = (asset: string, context: TranslateContext2) => { return writeElementBlock( { tagName: "div", attributes: `className="asset-info-item"` }, context => { context = addBuffer( ``, context ); // context = addBuffer(``, context); return context; }, context ); }; const writeColorBlock = ( name: string, value: string, context: TranslateContext2 ) => { const color = tc(value); const contrastedColor = color.isDark() ? "white" : "black"; context = writeElementBlock( { tagName: "div", attributes: `className="color-block" style="background-color: var(${name}); color: ${contrastedColor}"` }, context => { context = addBuffer(`var(${name})\n`, context); return context; }, context ); return context; }; const getFrameMixins = ( frame: any, styleType: string, context: TranslateContext2 ) => { const dep = context.graph[context.currentFileKey] as DesignDependency; const layersWithMixins = getLayersWithMixins(frame) as any[]; const info = {}; for (const layer of layersWithMixins) { const mixedInStyled = extractMixedInStyles(layer); for (const mixinId in mixedInStyled) { const mixin = getMixin(mixinId, dep, context.graph); if (mixin?.styleType === styleType) { info[getMixinName(mixin, context.options.config)] = getMixinValue( mixin, mixedInStyled[mixinId] ); } } } return info; }; const getLayersWithMixins = memoize((node: any) => { const flattened = []; const walk = (node: any) => { if (/^(INSTANCE|ELLIPSE|LINE|BOOLEAN_OPERATION|VECTOR)$/.test(node.type)) { return; } if (node.styles && Object.keys(node.styles).length) { flattened.push(node); } if (node.children) { node.children.forEach(walk); } }; walk(node); return flattened; }); const writeInfoSection = ( label: string, write: (context: TranslateContext2) => TranslateContext2, context: TranslateContext2 ) => { context = addBuffer(`

    ${label}

    \n`, context); context = addBuffer(`
    \n`, context); context = write(context); return context; }; const writeLayer = (layer: any, context: TranslateContext2) => { if (isExported(layer)) { return writeExportedLayer(layer, context); } switch (layer.type) { case "COMPONENT_SET": case "GROUP": case "COMPONENT": case "FRAME": case "RECTANGLE": return writeElementLayer(layer, context); case "INSTANCE": return writeInstanceLayer(layer, context); case "TEXT": return writeTextLayer(layer, context); case "ELLIPSE": case "LINE": case "BOOLEAN_OPERATION": case "VECTOR": return writeVectorLayer(layer, context); } return context; }; const writeExportedLayer = ( layer: any, context: TranslateContext2, instance = layer ) => { const assetPath = getLayerMediaPath( layer, getNodeDependency(layer, context.graph), layer.exportSettings[0] ); const attributes = `src="${path.relative( path.dirname(context.currentRelativeFilePath), assetPath )}"`; context = writeElementBlock( { tagName: "img", attributes }, context => { context = writeLayerStyle( instance, context, WriteLayerStyleMode.Position ); return context; }, context ); return context; }; const writeInstanceLayer = (layer: any, context: TranslateContext2) => { const component = getInstanceComponent( layer, getNodeDependency(layer, context.graph).fileKey, context.graph ); if (!component || !isExported(component)) { return writeElementLayer(layer, context); } return writeExportedLayer(component, context, layer); }; const writeVectorLayer = (layer: any, context: TranslateContext2) => { return context; }; const writeElementLayer = (layer: any, context: TranslateContext2) => { context = writeElementBlock( { tagName: "div" }, context => { context = writeLayerStyle(layer, context); if (layer.children) { for (const child of layer.children) { context = writeLayer(child, context); } } return context; }, context ); return context; }; enum WriteLayerStyleMode { Position = 1 << 2, Style = 2 << 2, Everything = (1 << 2) | (2 << 2) } const writeLayerStyle = ( layer: any, context: TranslateContext2, mode: WriteLayerStyleMode = WriteLayerStyleMode.Everything ) => { let style; [style, context] = getInlineStyle(layer, context, mode); context = writeElementBlock( { tagName: "style" }, context => { context = writeStyleDeclarations(style, context); return context; }, context ); return context; }; const writeTextLayer = (layer: any, context: TranslateContext2) => { context = writeElementBlock( { tagName: "span" }, context => { context = writeLayerStyle(layer, context); context = addBuffer(encodeURIComponent(layer.characters) + "\n", context); return context; }, context ); return context; }; const writePageImports = ( entry: DesignDependency, context: TranslateContext2 ) => { const importFilePaths = {}; Object.assign( importFilePaths, getDependencyImportFilePaths(entry, context.graph) ); importFilePaths[getDesignModulesFile(entry)] = 1; for (const relativePath in importFilePaths) { context = addBuffer( `\n`, context ); } context = addBuffer("\n", context); return context; }; const resolvePath = (to: string, context: TranslateContext2) => { let relative = path.relative( path.dirname(context.currentRelativeFilePath), to ); if (relative.charAt(0) !== ".") { relative = "./" + relative; } return relative; }; const writeFrameComment = ( layer: any, context: TranslateContext2, visible = true ) => { const { width, height, x, y } = layer.absoluteBoundingBox; context = { ...context, isFrame: true, framePosition: layer.absoluteBoundingBox }; context = addBuffer(`\n`, context); return context; }; const getDependencyImportFilePaths = ( entry: DesignDependency, graph: DependencyGraph ) => { const filePaths = {}; for (const refId in entry.imports) { const imp = entry.imports[refId]; const dep = graph[imp.fileKey]; if (dep.kind === DependencyKind.Font) { if (dep.content) { filePaths[getFontFile(dep)] = 1; } } else if (dep.kind === DependencyKind.Design) { filePaths[getDesignModulesFile(dep)] = 1; } } return filePaths; }; const getInlineStyle = ( layer: any, context: TranslateContext2, mode: WriteLayerStyleMode = WriteLayerStyleMode.Everything ) => { const { absoluteBoundingBox } = layer; const parent = getNodeParent( layer, getNodeDependency(layer, context.graph).document ); let style: any = {}; if (parent.type === "CANVAS" && mode & WriteLayerStyleMode.Position) { Object.assign(style, { width: `100vw`, height: `100vh` }); } else { const frame = getNodeFrame( layer, getNodeDependency(layer, context.graph).document ); Object.assign(style, { position: "fixed", width: px(absoluteBoundingBox.width), height: px(absoluteBoundingBox.height), left: px(absoluteBoundingBox.x - frame.absoluteBoundingBox.x), top: px(absoluteBoundingBox.y - frame.absoluteBoundingBox.y) }); } if (mode & WriteLayerStyleMode.Style) { Object.assign(style, getLayerStyle(layer)); [style, context] = addStyleMixins(layer, style, context); } return [style, context]; }; const addStyleMixins = (layer: any, style: any, context: TranslateContext2) => { const dep = getNodeDependency(layer, context.graph) as DesignDependency; let newStyle = { ...style }; // { mixinId: styleObject } const mixedInStyles = extractMixedInStyles(layer); for (const refId in mixedInStyles) { const imp = dep.imports[refId] as any; const mixinId = imp?.nodeId || refId; const impDep = (imp ? context.graph[imp.fileKey] : dep) as DesignDependency; const mixin = impDep.styles[mixinId]; const mixedInStyle = mixedInStyles[refId]; const atoms = getAtoms(impDep, context.graph, context.options.config); const mixinName = getMixinName(mixin, context.options.config); if (!atoms.mixins[mixinName] && !atoms.vars[mixinName]) { context = logContextWarn( `Could not find style ${chalk.bold(mixin.name)} in design ${chalk.bold( `${dep.name} / ${context.currentPageName}` )}`, context ); continue; } if (isStyleMixin(mixin)) { newStyle = omit(newStyle, Object.keys(mixedInStyle)); const path = [getDependencyImportId(impDep), mixinName]; newStyle = addInclude(path.join("."), newStyle); } else if (isStyleVar(mixin)) { for (const key in mixedInStyle) { newStyle[key] = `var(${mixinName})`; } } } return [newStyle, context]; }; const addInclude = (value: string, style: any) => { let newStyle = { ...style }; if (!newStyle["@include"]) { newStyle["@include"] = []; } newStyle["@include"].push(value); return newStyle; }; const getDependencyImportId = (dep: DesignDependency) => { return getImportId(getDesignModulesFile(dep)); }; const getImportId = (relativePath: string) => { return camelCase(relativePath.replace(/\.pc$/, "").replace("/index", "")); }; ================================================ FILE: packages/figma-paperclip/src/translate/utils.ts ================================================ import { kebabCase } from "lodash"; import { cleanLabel, Config, containsNode, DesignDependency, ExportSettings, FontDependency, getCleanedName, getNodeExportFileName, getNodePage } from "../state"; import { addBuffer, endBlock, startBlock, TranslateContext2 } from "./context"; export const getFontFile = (dep: FontDependency) => `typography/${dep.fileKey}.pc`; export const getDesignPageFile = (page: any, dep: DesignDependency) => { const name = kebabCase(cleanLabel(page.name)); // non-ascii chars, so ignore if (!name) { return null; } return `${kebabCase(cleanLabel(dep.name))}/${name}.pc`; }; export const getPageAtomsFile = (page: any, dep: DesignDependency) => { const name = kebabCase(cleanLabel(page.name)); // non-ascii chars, so ignore if (!name) { return null; } return `${kebabCase(cleanLabel(dep.name))}/${name}/index.pc`; }; export const getDesignModulesFile = (dep: DesignDependency) => { const name = kebabCase(cleanLabel(dep.name)); // non-ascii chars, so ignore if (!name) { return null; } return `${name}/index.pc`; }; export const getMixinValue = (mixin: any, style: any) => { if (isStyleMixin(mixin)) { return style; } else if (isStyleVar(mixin)) { return style.color || style.background || style.borderColor; } }; const getColorContrast = hexcolor => { // If a leading # is provided, remove it if (hexcolor.slice(0, 1) === "#") { hexcolor = hexcolor.slice(1); } // If a three-character hexcode, make six-character if (hexcolor.length === 3) { hexcolor = hexcolor .split("") .map(function(hex) { return hex + hex; }) .join(""); } // Convert to RGB value var r = parseInt(hexcolor.substr(0, 2), 16); var g = parseInt(hexcolor.substr(2, 2), 16); var b = parseInt(hexcolor.substr(4, 2), 16); // Get YIQ ratio var yiq = (r * 299 + g * 587 + b * 114) / 1000; // Check contrast return yiq >= 128 ? "black" : "white"; }; export const getLayerMediaPath = ( node, dep: DesignDependency, settings: ExportSettings ) => { return `${getCleanedName(dep.name)}/${getCleanedName( getNodePage(node.id, dep.document).name )}/${getNodeExportFileName(node, dep.document, settings)}`; }; export type WriteElementBlockParts = { tagName: string; attributes?: string; }; export const writeElementBlock = ( { tagName, attributes }: WriteElementBlockParts, writeBody: (context: TranslateContext2) => TranslateContext2, context: TranslateContext2 ) => { context = addBuffer( `<${tagName}${attributes ? " " + attributes : ""}>\n`, context ); context = startBlock(context); context = writeBody(context); context = endBlock(context); context = addBuffer(`\n`, context); return context; }; export const writeStyleDeclaration = ( name: string, value: string, context: TranslateContext2, format = true ) => { if (name === "@include") { return addBuffer(`@include ${value};\n`, context); } else { return addBuffer( `${format ? kebabCase(name) : name}: ${value};\n`, context ); } }; export const writeStyleDeclarations = ( style: any, context: TranslateContext2 ) => { for (const propertyName in style) { const value = style[propertyName]; if (Array.isArray(value)) { for (const part of value) { context = writeStyleDeclaration(propertyName, part, context); } } else { context = writeStyleDeclaration(propertyName, value, context); } } return context; }; export const writeStyleBlock = ( selector: string, writeBody: (context: TranslateContext2) => TranslateContext2, context: TranslateContext2 ) => { context = addBuffer(`${selector} {\n`, context); context = startBlock(context); context = writeBody(context); context = endBlock(context); context = addBuffer(`}\n`, context); return context; }; export const getStyleVarName = ({ name }, config: Config) => { if (config.atoms?.typePrefix) { name = `color-` + name; } return `--${kebabCase(name)}`; }; export const getStyleMixinName = ({ name, styleType }, config: Config) => { if (config.atoms?.typePrefix) { name = `${styleType}-` + name; } let varName = kebabCase(name); if (!isNaN(Number(varName.charAt(0)))) { varName = "_" + varName; } return varName; }; export const getMixinName = (style: any, config: Config) => { return isStyleVar(style) ? getStyleVarName(style, config) : getStyleMixinName(style, config); }; export const isStyleVar = (mixin: any) => { return mixin.styleType === "FILL"; }; export const isStyleMixin = (mixin: any) => { return mixin.styleType === "TEXT" || mixin.styleType === "EFFECT"; }; ================================================ FILE: packages/figma-paperclip/src/translate2/context.ts ================================================ import { uniq } from "lodash"; import { Config, DependencyGraph, ExcludeRule, OutputFile, OutputFileKind, Point } from "../state"; import { logWarn } from "../utils"; import * as chalk from "chalk"; import { memoize } from "../memo"; export const BLEND_MODE_MAP = { NORMAL: "normal", DARKEN: "darken", MULTIPLY: "multiply", COLOR_BURN: "color-burn", LIGHTEN: "lighten", SCREEN: "screen", COLOR_DODGE: "color-dodge", OVERLAY: "overlay", SOFT_LIGHT: "soft-light", HARD_LIGHT: "hard-light", DIFFERENCE: "difference", EXCLUSION: "exclusion", HUE: "hue", LUMINOSITY: "luminosity", SATURATION: "saturation", COLOR: "color" }; export type TranslateOptions = { includes: string[]; exclude: ExcludeRule[]; config: Config; }; export type TranslateContext = { content: string; options: TranslateOptions; graph: DependencyGraph; lineNumber: number; isNewLine: boolean; logs: string[]; indent: string; fileKey: string; mixins: Record; isFrame: boolean; framePosition?: Point; }; export type TranslateContext2 = { content: string; options: TranslateOptions; graph: DependencyGraph; lineNumber: number; currentRelativeFilePath?: string; isNewLine: boolean; indent: string; logs: string[]; currentFileKey?: string; currentPageName?: string; mixins: Record; isFrame: boolean; framePosition?: Point; files: OutputFile[]; }; export const ontext = ( fileKey: string, graph: DependencyGraph, options: TranslateOptions ): TranslateContext => ({ content: "", lineNumber: 0, fileKey, logs: [], graph, options, mixins: {}, isNewLine: false, indent: " ", isFrame: false }); export const startFile = ( currentRelativeFilePath: string, currentFileKey: string, context: TranslateContext2 ) => ({ ...context, currentFileKey, currentRelativeFilePath }); export const startPageFile = ( currentRelativeFilePath: string, currentFileKey: string, currentPageName: string, context: TranslateContext2 ): TranslateContext2 => ({ ...startFile(currentRelativeFilePath, currentFileKey, context), currentPageName }); export const addBuffer = (buffer: string, context: TranslateContext2) => ({ ...context, content: context.content + (context.isNewLine ? context.indent.repeat(context.lineNumber) : "") + buffer, isNewLine: buffer.lastIndexOf("\n") === buffer.length - 1 }); export const startBlock = (context: TranslateContext2) => ({ ...context, lineNumber: context.lineNumber + 1 }); export const endBlock = (context: TranslateContext2) => ({ ...context, lineNumber: context.lineNumber - 1 }); export const createContext = ( fileKey: string, graph: DependencyGraph, options: TranslateOptions ): TranslateContext => ({ content: "", lineNumber: 0, fileKey, graph, logs: [], options, mixins: {}, isNewLine: false, indent: " ", isFrame: false }); export const createContext2 = ( graph: DependencyGraph, options: TranslateOptions ): TranslateContext2 => ({ content: "", lineNumber: 0, logs: [], graph, files: [], options, mixins: {}, isNewLine: false, indent: " ", isFrame: false }); export const addFile = (context: TranslateContext2): TranslateContext2 => { return { ...context, content: "", lineNumber: 0, isNewLine: false, isFrame: false, currentRelativeFilePath: null, currentPageName: null, currentFileKey: null, files: [ ...context.files, { kind: OutputFileKind.Buffer, relativePath: context.currentRelativeFilePath, content: context.content } ] }; }; export const addRemoteFile = ( url: string, context: TranslateContext2 ): TranslateContext2 => { return { ...context, content: "", lineNumber: 0, isNewLine: false, isFrame: false, currentRelativeFilePath: null, currentPageName: null, currentFileKey: null, files: [ ...context.files, { kind: OutputFileKind.Remote, relativePath: context.currentRelativeFilePath, url } ] }; }; export const px = (value: number) => round(value, 2) + "px"; export const round = (value: number, dec: number) => parseFloat(value.toFixed(dec)); // TODO - need to create util for plucking out styles that are part of atoms export const getLayerStyle = memoize((layer: any) => { const style: any = {}; if (layer.type !== "TEXT") { if (layer.fills) { const value = getFillStyleValue(layer, layer.fills); if (value) { style.background = value; const containsBlendModes = layer.fills.some(fill => { return fill.blendMode !== "NORMAL"; }); if (containsBlendModes) { style.backgroundBlendMode = layer.fills .map(fill => { return BLEND_MODE_MAP[fill.blendMode]; }) .join(", "); } } } if (layer.effects) { const shadows = layer.effects.filter( effect => (effect.type === "DROP_SHADOW" || effect.type === "INNER_SHADOW") && effect.visible ); if (shadows.length) { style.boxShadow = shadows .map(shadow => { const buffer = []; if (shadow.type === "INNER_SHADOW") { buffer.push("inset"); } buffer.push( px(shadow.offset.x), px(shadow.offset.y), px(shadow.radius), getCSSRGBAColor(shadow.color) ); return buffer.join(" "); }) .join(", "); } } if (layer.rectangleCornerRadii) { if (uniq(layer.rectangleCornerRadii).length === 1) { style.borderRadius = px(layer.rectangleCornerRadii[0]); } else { style.borderTopLeftRadius = layer.rectangleCornerRadii[0] + "px"; style.borderTopRightRadius = layer.rectangleCornerRadii[1] + "px"; style.borderBottomLeftRadius = layer.rectangleCornerRadii[2] + "px"; style.borderBottomRightRadius = layer.rectangleCornerRadii[3] + "px"; } } } if (layer.type === "TEXT") { const solidFill = layer.fills.find(fill => fill.type === "SOLID"); if (solidFill) { style.color = getCSSRGBAColor(solidFill.color); } style.lineHeight = px(layer.style.lineHeightPx); style.fontFamily = layer.style.fontFamily + ", sans-serif"; style.fontWeight = layer.style.fontWeight; style.fontSize = px(layer.style.fontSize); style.textAlign = layer.style.textAlignHorizontal.toLowerCase(); style.letterSpacing = round(layer.style.letterSpacing, 2); } if (layer.strokes?.length) { const [stroke] = layer.strokes; if (stroke.color) { style.borderColor = getCSSRGBAColor(stroke.color); style.borderSyyle = stroke.type.toLowerCase(); style.borderWidth = layer.strokeWeight; } } return style; }); const getFillStyleValue = (layer: any, fills: any[]) => fills .reverse() .filter(fill => fill.visible !== false) .map((fill, index) => { switch (fill.type) { case "SOLID": { return getCSSRGBAColor(fill.color, index === fills.length - 1); } case "GRADIENT_LINEAR": { return getCSSLinearGradient(fill); } case "GRADIENT_RADIAL": { return getCSSRadialGradient(fill); } case "IMAGE": { return getCSSImageBackground(fill); } default: { // TODO - all gradient fills should work logLayerWarn(layer, `Cannot translate ${fill.type} fill to CSS`); return null; } } }) .filter(Boolean) .join(", "); const getCSSRGBAColor = ({ r, g, b, a }: any, last: boolean = true) => { const r2 = Math.round(r * 255); const g2 = Math.round(g * 255); const b2 = Math.round(b * 255); // TODO - generate hash let color; if (a !== 1) { color = `rgba(${r2}, ${g2}, ${b2}, ${round(a, 2)})`; } else { color = rgbToHex(r2, g2, b2); } return last ? color : `linear-gradient(0deg, ${color}, ${color})`; }; const getCSSImageBackground = ({ imageRef }: any) => { // TODO: https://github.com/crcn/figmark/issues/13 // TODO - need to get actual extension info. return `url("./${imageRef}.png")`; }; const getCSSRadialGradient = ({ gradientStops }: any) => { // TODO: https://github.com/crcn/figmark/issues/13 return `radial-gradient(${gradientStops .map(stop => { return `${getCSSRGBAColor(stop.color)} ${stop.position * 100}%`; }) .join(", ")})`; }; const rgbToHex = (r: number, g: number, b: number) => { return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); }; const logLayerWarn = (node: any, message: string) => { logWarn(`Layer ${chalk.bold(node.name)}: ${message}`); }; const getCSSLinearGradient = ({ gradientHandlePositions, gradientStops }: any) => { // TODO: https://github.com/crcn/figmark/issues/12 const radians = calcGradiantHandleRadians(gradientHandlePositions); return `linear-gradient(${radians}rad, ${gradientStops .map(stop => { return `${getCSSRGBAColor(stop.color)} ${stop.position * 100}%`; }) .join(", ")})`; }; const calcGradiantHandleRadians = ([first, second]: any) => { const ydiff = second.y - first.y; const xdiff = first.x - second.x; const radians = Math.atan2(-xdiff, -ydiff); return Number(radians.toFixed(3)); }; export const logContextWarn = (text: string, context: TranslateContext2) => { if (context.logs.includes(text)) { return context; } context = { ...context, logs: [...context.logs, text] }; logWarn(text); return context; }; ================================================ FILE: packages/figma-paperclip/src/translate2/index.ts ================================================ import { DependencyGraph, DependencyKind, getNodeById, getNodeDependencyById } from "../state"; import { startFile } from "../translate/context"; import { getLayerMediaPath } from "../translate/utils"; import { writePages } from "./pages"; import { addRemoteFile, createContext2, TranslateContext2, TranslateOptions } from "./context"; export const translateFigmaGraph2 = ( graph: DependencyGraph, options: TranslateOptions ) => { let context = createContext2(graph, options); context = writePages(graph, context); context = writeMedia(context); return context.files; }; const writeMedia = (context: TranslateContext2) => { for (const key in context.graph) { const dep = context.graph[key]; if (dep.kind === DependencyKind.Media) { const nodeDep = getNodeDependencyById(dep.nodeId, context.graph); const node = getNodeById(dep.nodeId, nodeDep.document); context = startFile( getLayerMediaPath(node, nodeDep, dep.settings), key, context ); context = addRemoteFile(dep.url, context); } } return context; }; ================================================ FILE: packages/figma-paperclip/src/translate2/pages.ts ================================================ import { writeFile } from "fs-extra"; import { kebabCase } from "lodash"; import { DependencyGraph, DependencyKind, DesignDependency } from "../state"; import { addBuffer } from "../translate/context"; import { getDesignPageFile, getPageAtomsFile, writeElementBlock, writeStyleBlock, writeStyleDeclaration, writeStyleDeclarations } from "../translate/utils"; import { addFile, startFile, TranslateContext2 } from "./context"; import { Atom, AtomGroup, AtomType, Category, createAtoms, createAtomsFromPage } from "./state"; export const writePages = ( graph: DependencyGraph, context: TranslateContext2 ) => { for (const uri in graph) { const dep = graph[uri]; if (dep.kind === DependencyKind.Design) { context = writeDesign(dep, graph, context); } } // context = startFile("atoms.pc", null, context); // context = writeAtomStyleBlock(graph, context); // context = addFile(context); return context; }; const writeDesign = ( dep: DesignDependency, graph: DependencyGraph, context: TranslateContext2 ) => { for (const page of dep.document.children) { context = writePage(page, dep, graph, context); } return context; }; const writePage = ( page: any, dep: DesignDependency, graph: DependencyGraph, context: TranslateContext2 ) => { const atomGroups = createAtomsFromPage(page, context.options.config); if (!atomGroups.length) { return context; } context = startFile(getPageAtomsFile(page, dep), dep.fileKey, context); context = writeAtomStyleBlock(atomGroups, context); context = addFile(context); return context; }; const writeAtomStyleBlock = (atomGroups: any, context: TranslateContext2) => { context = writeElementBlock( { tagName: "style" }, context => { for (const atom of atomGroups) { context = writeAtomGroup(atom, context); } return context; }, context ); return context; }; const writeAtomGroup = (group: AtomGroup, context: TranslateContext2) => { const category = group.name as Category; context = writeStyleBlock( "@export", context => { return writeAtom(0, category, group, context); }, context ); context = addBuffer("\n", context); return context; }; const writeAtom = ( depth: number, category: string, atom: Atom, context: TranslateContext2 ) => { switch (atom.type) { case AtomType.Group: { const writeChildren = (context: TranslateContext2) => atom.children.reduce( (context, child) => writeAtom(depth + 1, category, child, context), context ); // don't allow for variants for typography since Mixins can't // be nested with style rules. if (category === Category.Typography) { return writeChildren(context); } let selector: string; if (depth === 0) { selector = ":root"; if (atom.children.length && atom.children[0].type === AtomType.Group) { selector = "*"; } } else { selector = "&." + kebabCase(atom.name); } if (context.options.config.atoms.globalVars) { selector = `:global(${selector})`; } return writeStyleBlock(selector, writeChildren, context); } case AtomType.Color: { return writeStyleDeclaration( `--${kebabCase(atom.name)}`, atom.value, context, false ); } case AtomType.Shadow: { return writeStyleDeclaration( `--${kebabCase(atom.name)}`, atom.value, context, false ); } case AtomType.Typography: { context = writeStyleBlock( "." + kebabCase(atom.name), context => { return writeStyleDeclarations(atom.properties, context); }, context ); context = writeStyleBlock( "@mixin " + kebabCase(atom.name), context => { return writeStyleDeclarations(atom.properties, context); }, context ); return context; } } return context; }; ================================================ FILE: packages/figma-paperclip/src/translate2/state.ts ================================================ import { pick } from "lodash"; import { memoize } from "../memo"; import { Canvas, Config, DependencyGraph, DependencyKind, DesignDependency, flattenNodes, isVectorLike, Node } from "../state"; import { getLayerStyle } from "../translate/context"; export enum AtomType { Color = "Color", Shadow = "Shadow", Typography = "Typography", Asset = "Asset", Group = "Group", Root = "Root" } export enum Category { Colors = "Colors", Shadows = "Shadows", Typography = "Typography" } export type BaseAtom = { type: TType; name: string; }; export type Asset = BaseAtom & { value: string; }; export type Color = BaseAtom & { value: string; }; export type Shadow = BaseAtom & { value: string; }; export type Typography = BaseAtom & { properties: Record; }; export type AtomGroup = BaseAtom & { children: Atom[]; }; export type AtomRoot = { dependencyAtoms: Record; }; export type Atom = AtomGroup | Asset | Color | Shadow | Typography; type GenerateOptions = { prefix: string; }; export const createAtoms = (graph: DependencyGraph, config: Config) => { return createAtomRoot(graph, { prefix: config.atoms?.prefix || "$" }); }; const createAtomRoot = ( graph: DependencyGraph, options: GenerateOptions ): AtomGroup[] => { const atoms: AtomGroup[] = []; for (const uri in graph) { const dep = graph[uri]; if (dep.kind === DependencyKind.Design) { atoms.push(...createAtomsFromDesign(dep, options)); } } return atoms; }; export const createAtomsFromDesign = ( design: DesignDependency, options: GenerateOptions ) => { const atoms: AtomGroup[] = []; for (const page of design.document.children) { atoms.push(...createAtomsFromPage2(page, options)); } return atoms; }; export const createAtomsFromPage = (page: any, config: Config) => createAtomsFromPage2(page, { prefix: config.atoms?.prefix || "" }); const createAtomsFromPage2 = ( page: any, options: GenerateOptions ): AtomGroup[] => { const atoms: AtomGroup[] = []; if (hasCanvasCategory(page, options)) { const atom = createAtomFromCanvas(page, options); if (atom) { atoms.push(atom); } } else { for (const canvas of page.children) { const atom = createAtomFromCanvas(canvas, options); if (!atom) { continue; } atoms.push(atom); } } return atoms; }; const createAtomFromCanvas = ( canvas: Canvas, options: GenerateOptions ): AtomGroup => { const category = getCanvasCategory(canvas, options); if (!category || !canvas.children) { return null; } return { type: AtomType.Group, name: category, children: createAtomGroupChildren(1, canvas, options) }; }; const createAtomGroupChildren = ( index: number, canvas: Canvas, options: GenerateOptions ) => { const nameParts = getCanvasNameParts(canvas); if (index === nameParts.length) { return createCanvasAtoms(canvas, options); } else { return [ { type: AtomType.Group, name: nameParts[index], children: createAtomGroupChildren(index + 1, canvas, options) } ]; } }; const createCanvasAtoms = (canvas: Canvas, options: GenerateOptions) => { return ( flattenNodes(canvas) // first child is the canvas, skip that. .slice(1) .filter(isAtom(options)) .map(node => { const category = getCanvasCategory(canvas, options); const model = getAtomModel(node, category); return ( model && createAtom( stripPrefix(node.name, options.prefix), getLayerStyle(model), category ) ); }) .filter(Boolean) ); }; const createAtom = (name: string, style: any, category: Category): Atom => { switch (category) { case Category.Colors: { return { type: AtomType.Color, name, value: style.background || "invalid" }; } case Category.Shadows: { return { type: AtomType.Shadow, name, value: style.boxShadow || "invalid" }; } case Category.Typography: { return { type: AtomType.Typography, name, properties: pick( style, "fontFamily", "letterSpacing", "lineHeight", "fontWeight", "fontSize", "textAlign" ) }; } } }; const getAtomModel = memoize((node: Node, category: Category) => { let filter; if (category === Category.Colors || category === Category.Shadows) { filter = node => { return isVectorLike(node) || node.type === "RECTANGLE"; }; } else if (category === Category.Typography) { filter = node => { return node.type === "TEXT"; }; } return flattenNodes(node).find(filter); }); const isAtom = memoize((options: GenerateOptions) => memoize((node: Node) => { const name = stripPrefix(node.name, options.prefix); return name != null; }) ); const getCanvasCategory = memoize( (canvas: Node, options: GenerateOptions): Category => { const nameParts = getCanvasNameParts(canvas); const category = Category[maybeTitlecase(stripPrefix(nameParts[0], options.prefix))]; if (!category) { return null; } return category; } ); export const hasCanvasCategory = (canvas: Node, options: GenerateOptions) => Boolean(getCanvasCategory(canvas, options)); const getCanvasNameParts = memoize((canvas: Node) => { return canvas.name.toLowerCase().split(/\s*\/\s*/); }); const stripPrefix = (value: string, prefix: string) => { if (value.indexOf(prefix) !== 0) { return null; } return value.substr(prefix.length); }; const maybeTitlecase = str => str && str.charAt(0).toUpperCase() + str.substr(1).toLowerCase(); ================================================ FILE: packages/figma-paperclip/src/utils.ts ================================================ import * as path from "path"; import * as fsa from "fs-extra"; import * as https from "https"; import * as chalk from "chalk"; import * as crypto from "crypto"; import { Config, Exportable, Node, NodeType } from "./state"; import { camelCase } from "lodash"; export const CONFIG_FILE_NAME = "figma-paperclip.config.js"; export const COMMAND_NAME = "figma-paperclip"; export const readConfig = (cwd: string): Config => { return require(getConfigPath(cwd)); }; export const getConfigPath = (cwd: string) => path.join(cwd, CONFIG_FILE_NAME); export const configFileExists = (cwd: string) => fsa.existsSync(getConfigPath(cwd)); export const logWarn = (message: string) => { console.warn(chalk.yellow(`warn`) + " " + message); }; export const logInfo = (message: string) => { console.warn(chalk.blue(`info`) + " " + message); }; export const logVerb = (message: string) => { console.warn(chalk.grey(`verb`) + " " + message); }; export const logSuccess = (message: string) => { console.warn(chalk.green(`succ`) + " " + message); }; export const logError = (message: string) => { console.warn(chalk.red(`fail`) + " " + message); }; export const pascalCase = (value: string) => value.charAt(0).toUpperCase() + camelCase(value.substr(1)); export const md5 = (value: string) => { return crypto .createHash("md5") .update(value) .digest("hex"); }; export type SourceUrlInfo = Partial<{ fileKey?: string; projectId?: number; name?: string; teamId?: string; }>; export const extractSourceUrlInfo = (url: string): SourceUrlInfo => { const matches1 = url.match( /https:\/\/www.figma.com\/files\/(.*?)\/team\/(.*?)\// ); let fileKey: string; let teamId: string; let name: string; let projectId: number; if (matches1) { // projectId = matches1[1]; teamId = matches1[2]; } const matches2 = url.match( /https:\/\/www.figma.com\/files\/(.*?)\/project\/(.*?)\/(.*?)/ ); if (matches2) { fileKey = matches2[2]; name = matches2[3]; // teamId = matches2[2]; } const matches3 = url.match( /https:\/\/www.figma.com\/files\/project\/(.*?)\/(.*?)/ ); if (matches3) { projectId = Number(matches3[1]); name = matches3[2]; } const matches4 = url.match( /https:\/\/www.figma.com\/file\/(.*?)\/([^\?\/]+)/ ); if (matches4) { fileKey = matches4[1]; name = matches4[2]; } const matches5 = url.match( /https\:\/\/www.figma.com\/files\/team\/(.*?)\/([^\?\/]+)/ ); if (matches5) { teamId = matches5[1]; name = matches5[2]; } return { fileKey, projectId, name, teamId }; }; export const findLayer = (layer: any, filter: (layer: any) => boolean) => { if (filter(layer)) { return layer; } if (layer.children) { for (const child of layer.children) { const found = findLayer(child, filter); if (found) return found; } } }; export const httpGet = (options: https.RequestOptions): Promise => { return new Promise((resolve, reject) => { https.get(options, res => { let buffer = ""; res.on("data", chunk => (buffer += String(chunk))); res.on("end", () => { if (res.statusCode === 200) { try { const result = JSON.parse(buffer); resolve(result); } catch (e) { resolve(buffer); } } else { try { reject(JSON.parse(buffer)); } catch (e) { reject(buffer); } } }); }); }); }; export const isExported = (node: Node): node is Exportable => { return node.type !== NodeType.Document && node.exportSettings?.length > 0; }; ================================================ FILE: packages/figma-paperclip/todos.md ================================================ ================================================ FILE: packages/figma-paperclip/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./lib", "target": "es5", "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true, "strict": false /* Enable all strict type-checking options. */ }, "exclude": [ "**/*.d.ts", "lib/**/*.d.ts", "esm", "native", "lib/**/*", "node_modules", "*-test", "index.d.ts", "browser.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/jest-paperclip/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/jest-paperclip/package.json ================================================ { "name": "jest-paperclip", "version": "18.5.6", "description": "Jest Paperclip plugin", "main": "lib/index.js", "publishConfig": { "access": "public" }, "scripts": { "build": "tsc", "build:watch": "tsc --watch --preserveWatchOutput" }, "author": "", "license": "ISC", "dependencies": { "@babel/core": "^7.11.1", "@babel/preset-env": "^7.11.0", "@paperclip-ui/builder": "^18.5.6", "@paperclip-ui/core": "^18.5.6", "resolve": "^1.17.0", "typescript": "^4.2.3" }, "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a" } ================================================ FILE: packages/jest-paperclip/src/index.ts ================================================ import * as url from "url"; import * as fs from "fs"; import * as babel from "@babel/core"; import * as path from "path"; import { buildCompilerOptions } from "@paperclip-ui/utils"; import { createEngineDelegate, PC_CONFIG_FILE_NAME, findPCConfigUrl, PaperclipConfig } from "@paperclip-ui/core"; import { buildFile } from "@paperclip-ui/builder"; const engine = createEngineDelegate(); module.exports = { process(_content, fullPath) { const fileUri = url.pathToFileURL(fullPath).href; const pcUrl = findPCConfigUrl(fs)(fullPath); if (!pcUrl) { throw new Error(`Unable to config ${PC_CONFIG_FILE_NAME}`); } const config: PaperclipConfig = JSON.parse( fs.readFileSync(url.fileURLToPath(pcUrl), "utf8") ); let code; // somewhat of a dumb approach to figuring out what emits. JS will want to be more specific about this at // some point. Maybe pulling in env var as a good stop gap? for (const targetCompilerOptions of buildCompilerOptions(config)) { if ( targetCompilerOptions.emit && !targetCompilerOptions.emit.includes("js") ) { continue; } code = buildFile(fileUri, engine, { cwd: path.dirname(fullPath), config, targetCompilerOptions }).translations[".js"]; if (code) { break; } } engine.open(fileUri); return babel.transformSync(code, { presets: ["@babel/env"], configFile: false }).code; } }; ================================================ FILE: packages/jest-paperclip/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./lib", "target": "es5", "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true }, "exclude": [ "**/*.d.ts", "esm", "native", "lib", "node_modules", "*-test", "index.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-autocomplete/.eslintrc.json ================================================ { "root": true, "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], "rules": { "no-constant-condition": 0 } } ================================================ FILE: packages/paperclip-autocomplete/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/paperclip-autocomplete/README.md ================================================ Internal autocomplete library for Paperclip Demo: ```typescript import { getSuggestions } from "@paperclip-ui/complete"; let source = ` a { } `, { kind: SuggestContextKind.HTML_CLOSE_TAG_NAME, openTagPath: ["div"], }, ], [ `
    `, { kind: SuggestContextKind.HTML_CLOSE_TAG_NAME, openTagPath: ["span"], }, ], [ `
    `, { kind: SuggestContextKind.HTML_CLOSE_TAG_NAME, openTagPath: ["div"], }, ], [ `
    `, { kind: SuggestContextKind.HTML_CLOSE_TAG_NAME, openTagPath: ["b"], }, ], [ `
    ffdfd dsd{cffd }`, { kind: SuggestContextKind.HTML_CLOSE_TAG_NAME, openTagPath: ["b"], }, ], [ `
    `, { kind: SuggestContextKind.HTML_CLOSE_TAG_NAME, openTagPath: ["div"], }, ], [ `<`, { kind: SuggestContextKind.HTML_TAG_NAME, path: [], }, ], [ `
    <`, { kind: SuggestContextKind.HTML_TAG_NAME, path: [], }, ], [ `
    <`, { kind: SuggestContextKind.HTML_TAG_NAME, path: [], }, ], [ ` <`, { kind: SuggestContextKind.HTML_TAG_NAME, path: [], }, ], [ `
    <`, { kind: SuggestContextKind.HTML_TAG_NAME, path: [], }, ], [ `
    {children}!
    Hello Paperclip! ================================================ FILE: packages/paperclip-cli/src/scaffolding/generators/files/tsconfig.json ================================================ { "compilerOptions": { "outDir": "lib", "target": "es5", "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true, "jsx": "react" }, "exclude": [ "node_modules", "*-test", "lib", "index.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-cli/src/scaffolding/generators/files/webpack.config-js ================================================ const path = require("path"); const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); /* * We've enabled HtmlWebpackPlugin for you! This generates a html * page for you when you compile webpack, which will make you start * developing and prototyping faster. * * https://github.com/jantimon/html-webpack-plugin * */ module.exports = { mode: "development", entry: "{{ENTRY}}", output: { filename: "[name].js", path: path.resolve(__dirname, "dist") }, devtool: false, plugins: [new HtmlWebpackPlugin()], module: { rules: [ {{RULES}} { test: /\.pc$/, loader: "paperclip-loader", include: [path.resolve(__dirname, "src")], exclude: [/node_modules/], options: { config: require("./paperclip.config.json") } }, { test: /\.css$/, use: ["style-loader", "css-loader"] }, { test: /\.(png|jpe?g|gif)$/i, use: [ { loader: "file-loader" } ] } ] }, resolve: { extensions: [".jsx", ".tsx", ".ts", ".js"] } }; ================================================ FILE: packages/paperclip-cli/src/scaffolding/generators/index.ts ================================================ export * from "./base"; export * from "./root"; ================================================ FILE: packages/paperclip-cli/src/scaffolding/generators/javascript.ts ================================================ import * as fsa from "fs-extra"; import * as path from "path"; import { GeneratorKind } from "./base"; import { FILES_DIR } from "./utils"; const BABELRC_CONTENT = fsa.readFileSync( path.join(FILES_DIR, ".babelrc"), "utf8" ); export const javascript = { kind: GeneratorKind.JavaScript, prepare() { return { [GeneratorKind.Node]: { devDependencies: [ "babel-loader", "@babel/core", "@babel/preset-react", "@babel/preset-env" ] }, [GeneratorKind.Webpack]: { rules: `{ test: /\\.(js|jsx)$/, exclude: /node_modules/, use: ['babel-loader'] }` } }; }, generate() { return { ".babelrc": BABELRC_CONTENT }; } }; ================================================ FILE: packages/paperclip-cli/src/scaffolding/generators/node.ts ================================================ import { GeneratorKind } from "./base"; import { prompt } from "inquirer"; import { webpack } from "./webpack"; import { react } from "./react"; import { typescript } from "./typescript"; import { javascript } from "./javascript"; import * as shell from "shelljs"; import * as path from "path"; import * as fs from "fs"; import chalk from "chalk"; import { kebabCase } from "lodash"; import { installDep } from "./utils"; const readPackage = (cwd: string) => { const pkgPath = path.join(cwd, "package.json"); return fs.existsSync(pkgPath) ? fs.readFileSync(pkgPath, "utf8") : null; }; export const node = { kind: GeneratorKind.Node, async getParams({ cwd, isNewDirectory }) { const options = []; if (!readPackage(cwd)) { options.push( { name: "name", message: "Project name", validate: Boolean }, { name: "description", message: "Project description" }, { name: "isPrivate", message: "Is this package private?", type: "confirm", default: true }, { name: "useTypescript", message: "Use TypeScript?", type: "confirm" } ); } const hasYarnLock = fs.existsSync(path.join(cwd, "yarn.lock")); const hasPackageLock = fs.existsSync(path.join(cwd, "package-lock.json")); if (!hasYarnLock && !hasPackageLock) { options.push({ name: "packageManager", message: "What package manager?", type: "list", choices: ["npm", "yarn"] }); } const { name, description, packageManager = hasYarnLock ? "yarn" : "npm", useTypescript, isPrivate } = await prompt(options); return [ { cwd, name, isPrivate, useTypescript, packageManager, description, license: "" }, [ useTypescript ? typescript : useTypescript === false ? javascript : null, isNewDirectory ? webpack : null, react ].filter(Boolean) ]; }, prepare(params) { return { [GeneratorKind.Node]: { ...params, devDependencies: [], scripts: params.useTypescript ? { build: ["paperclip build"], start: ["paperclip build --watch"] } : {} } }; }, generate({ name, description, isPrivate, license, scripts, cwd }: any) { // ignore if existing package? if (readPackage(cwd)) { return {}; } const pkg = { name: kebabCase(name), description, version: "1.0.0", private: isPrivate, scripts: Object.keys(scripts || {}).reduce( (joinedScripts, scriptName) => { if (scripts[scriptName].length > 1) { joinedScripts[scriptName] = `concurrently ${scripts[scriptName] .map(script => { return `"${script}"`; }) .join(" ")}`; } else { joinedScripts[scriptName] = scripts[scriptName].join(""); } return joinedScripts; }, {} ), license, dependencies: {}, devDependencies: {} }; return { "package.json": JSON.stringify(pkg, null, 2) }; }, async install({ packageManager, cwd, devDependencies, dependencies }) { const pm = packageManager; await this._installDeps(pm, dependencies, false, cwd); await this._installDeps(pm, devDependencies, true, cwd); }, _installDeps(pm, dependencies, dev, cwd) { return installDep(dependencies, cwd, pm, dev); }, async postinstall( { cwd, packageManager }, { [GeneratorKind.Root]: { overwrite } } ) { return { [GeneratorKind.Node]: overwrite ? { completionMessage: `🎉 All done! Go ahead and run ${chalk.bold( `${packageManager} start` )}` } : {} }; }, fin({ completionMessage }) { if (completionMessage) { console.info(completionMessage); } } }; ================================================ FILE: packages/paperclip-cli/src/scaffolding/generators/percy.ts ================================================ import { GeneratorKind } from "./base"; // TODO - prompt to setup Git hooks export const percy = { kind: GeneratorKind.Percy, prepare() { return { [GeneratorKind.Node]: { scripts: { snapshot: ["percy exec -- percy-paperclip"] }, devDependencies: ["percy", "percy-paperclip"] } }; } }; ================================================ FILE: packages/paperclip-cli/src/scaffolding/generators/react.ts ================================================ import * as fsa from "fs-extra"; import * as path from "path"; import { GeneratorKind } from "./base"; import { FILES_DIR } from "./utils"; const ENTRY_TSX_CONTENT = fsa.readFileSync( path.join(FILES_DIR, "entry-tsx"), "utf8" ); export const react = { kind: GeneratorKind.React, getParams({ cwd }) { return [{ cwd }, []]; }, prepare({ cwd }, allInfo) { const devDependencies = ["@paperclip-ui/compiler-react"]; if (allInfo[GeneratorKind.Node].useTypescript) { devDependencies.push("@types/react", "@types/react-dom"); } return { [GeneratorKind.React]: { cwd }, [GeneratorKind.Node]: { dependencies: ["react", "react-dom"], devDependencies }, [GeneratorKind.Webpack]: { entry: this.getEntry(allInfo) }, [GeneratorKind.Root]: { compilerName: "@paperclip-ui/compiler-react" } }; }, getEntry(allInfo) { return ( allInfo[GeneratorKind.Root].sourceDirectory + "/entry." + (allInfo[GeneratorKind.Node].useTypescript ? "tsx" : "jsx") ); }, generate({ cwd }, allInfo) { if (fsa.existsSync(path.join(cwd, "package.json"))) { return {}; } return { [this.getEntry(allInfo)]: ENTRY_TSX_CONTENT.replace( "{{PROJECT_NAME}}", allInfo[GeneratorKind.Node].name ) }; } }; ================================================ FILE: packages/paperclip-cli/src/scaffolding/generators/root.ts ================================================ import * as fsa from "fs-extra"; import chalk from "chalk"; import * as path from "path"; import { FILES_DIR, installDep } from "./utils"; import { GeneratorKind } from "./base"; import { prompt } from "inquirer"; import { PaperclipConfig } from "@paperclip-ui/core"; const HELLO_WORLD_CONTENT = fsa.readFileSync( path.join(FILES_DIR, "hello-paperclip.pc"), "utf8" ); type Params = { cwd: string; outDir: string; srcDir: string; compilerName: string; }; export const root = { kind: GeneratorKind.Root, async getParams({ cwd, isNewDirectory }): Promise<[Params, any]> { const { srcDir, outDir, compilerName } = await prompt([ { name: "srcDir", message: "Paperclip source directory", default: "./src" }, { name: "outDir", message: "Paperclip compile directory", default: "./lib" }, { name: "compilerName", type: "list", message: "Is there a compiler that you'd like to use?", choices: [ { name: "None", value: undefined }, { name: "React", value: "@paperclip-ui/compiler-react" }, { name: "HTML", value: "@paperclip-ui/compiler-html" } ] } ]); return [ { cwd, srcDir, outDir, compilerName }, [] ]; }, prepare(params) { return { [GeneratorKind.Root]: params }; }, postinstall(params) { return { [GeneratorKind.Root]: params }; }, async install({ compilerName, cwd }: any) { await installDep( ["@paperclip-ui/core", "@paperclip-ui/cli"], cwd, null, true ); if (compilerName) { await installDep([compilerName], cwd, null, true); } }, async generate({ srcDir, outDir, compilerName, cwd }: Params) { const config: PaperclipConfig = { compilerOptions: [ { target: compilerName?.replace("@paperclip-ui/compiler-", ""), outDir } ], srcDir }; const files = { [srcDir + "/hello-paperclip.pc"]: HELLO_WORLD_CONTENT }; if (!fsa.existsSync(path.join(cwd, "paperclip.config.json"))) { files["paperclip.config.json"] = JSON.stringify(config, null, 2); } return files; }, fin({ overwrite }) { if (!overwrite) { console.info("🎉 All set up!"); console.info(""); console.info( chalk.cyanBright( "Next just run " + chalk.underline("npx @paperclip-ui/cli build") + " to generate code! More docs on this can be found here: \n\n" + chalk.underline(`https://paperclip.dev/docs/`) ) ); console.info(""); } } }; ================================================ FILE: packages/paperclip-cli/src/scaffolding/generators/typescript.ts ================================================ import * as fsa from "fs-extra"; import * as path from "path"; import { GeneratorKind } from "./base"; import { FILES_DIR } from "./utils"; const TSCONFIG_CONTENT = fsa.readFileSync( path.join(FILES_DIR, "tsconfig.json"), "utf8" ); export const typescript = { kind: GeneratorKind.TypeScript, prepare() { return { [GeneratorKind.Node]: { devDependencies: ["typescript", "ts-loader"] }, [GeneratorKind.Webpack]: { rules: `{ test: /\\.(ts|tsx)?$/, loader: "ts-loader", include: [path.resolve(__dirname, "src")], exclude: [/node_modules/] }` } }; }, generate() { return { "tsconfig.json": TSCONFIG_CONTENT }; } }; ================================================ FILE: packages/paperclip-cli/src/scaffolding/generators/utils.ts ================================================ import * as fs from "fs"; import * as shell from "shelljs"; import * as path from "path"; export const FILES_DIR = path.join( __dirname, "../../../src/scaffolding/generators/files" ); export const isUsingYarn = (cwd: string) => { return fs.existsSync(path.join(cwd, "yarn.lock")); }; export const installDep = ( names: string[], cwd: string, packageManager?: string, dev?: boolean ) => { if (!packageManager) { packageManager = isUsingYarn(cwd) ? "yarn" : "npm"; } let cmd: string; let saveFlags: string; if (packageManager === "npm") { cmd = "npm install"; saveFlags = dev ? "--save-dev" : "--save"; } else { cmd = "yarn add"; saveFlags = "--dev"; } const execCommand = `${cmd} ${names.join(" ")} ${saveFlags}`; console.info(`🔗 ${execCommand}`); return shell.exec(execCommand, { cwd }); }; ================================================ FILE: packages/paperclip-cli/src/scaffolding/generators/webpack.ts ================================================ import * as fsa from "fs-extra"; import * as path from "path"; import { GeneratorKind } from "./base"; import { FILES_DIR } from "./utils"; const WEBPACK_JS_CONTENT = fsa.readFileSync( path.join(FILES_DIR, "webpack.config-js"), "utf8" ); export const webpack = { kind: GeneratorKind.Webpack, getParams({ cwd }) { return [{ cwd }, []]; }, prepare(params) { return { [GeneratorKind.Webpack]: { ...params }, [GeneratorKind.Node]: { devDependencies: [ "webpack", "webpack-dev-server", "paperclip-loader", "html-webpack-plugin", "webpack-cli", "css-loader", "style-loader", "file-loader" ], scripts: { build: ["webpack"], "build:watch": ["webpack --watch"], start: ["webpack serve --open"] } } }; }, generate({ rules, entry, cwd }) { if (fsa.existsSync(path.join(cwd, "package.json"))) { return {}; } return { "webpack.config.js": WEBPACK_JS_CONTENT.replace( "{{ENTRY}}", entry ).replace("{{RULES}}", rules && rules.length ? rules + "," : "") }; } }; ================================================ FILE: packages/paperclip-cli/src/scaffolding/index.ts ================================================ import * as path from "path"; import * as fsa from "fs-extra"; import { root } from "./generators"; import { mergeWith } from "lodash"; export type GenerateProjectFileOptions = { isNewDirectory: boolean; cwd: string; }; export const generateProjectFiles = async ( options: GenerateProjectFileOptions ) => { const current: any = [root]; const allParams = {}; const usedGenerators = {}; // get params while (current.length) { const generator = current.shift(); usedGenerators[generator.kind] = generator; if (generator.getParams) { const [params, generators] = await generator.getParams(options); allParams[generator.kind] = params; current.push(...generators); } } const prepInfo = await run(usedGenerators, "prepare", allParams); const files = await run(usedGenerators, "generate", prepInfo); // WRITE for (const relativePath in files) { const content = files[relativePath]; if (!content) { continue; } const absPath = path.join(options.cwd, relativePath); // Normalize relative dirs console.info(`✍️ Writing ${path.relative(options.cwd, absPath)}`); fsa.mkdirpSync(path.dirname(absPath)); fsa.writeFileSync(absPath, content); } await run(usedGenerators, "install", prepInfo); const postInstallInfo = await run(usedGenerators, "postinstall", prepInfo); await run(usedGenerators, "fin", postInstallInfo); }; const run = async (usedGenerators, script, prepInfo) => { let allResults = {}; for (const kind in usedGenerators) { const gen = usedGenerators[kind]; if (gen[script]) { const result = (await usedGenerators[kind][script](prepInfo[kind], prepInfo)) || {}; allResults = mergeWith(allResults, result, (objValue, srcValue) => { if (Array.isArray(objValue)) { return objValue.concat(srcValue); } }); } } return allResults; }; ================================================ FILE: packages/paperclip-cli/src/test/basic-test.ts ================================================ ================================================ FILE: packages/paperclip-cli/tsconfig.json ================================================ { "compilerOptions": { "outDir": "lib", "target": "es5", "module": "commonjs", "esModuleInterop": true, "jsx": "react" }, "exclude": [ "node_modules", "*-test", "lib", "index.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-cli-minimal/.eslintrc.json ================================================ { "root": true, "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], "rules": { "no-constant-condition": 0, "@typescript-eslint/no-var-requires": 0, "@typescript-eslint/no-empty-function": 0 } } ================================================ FILE: packages/paperclip-cli-minimal/.npmignore ================================================ ================================================ FILE: packages/paperclip-cli-minimal/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/paperclip-cli-minimal/README.md ================================================ CLI tool for compiling paperclip templates. Installation: `npm install @paperclip-ui/cli --save-dev` Documentation: http://paperclip.dev/docs/usage-cli ================================================ FILE: packages/paperclip-cli-minimal/bin/paperclip ================================================ #!/usr/bin/env node const path = require("path"); const {build} = require("../lib/cli"); const argv = require("yargs") .command("build [file-pattern]", "Compiles paperclip files to target language", yargs => { yargs .option("watch", desc("watch for changes")) .option("print", desc("Only print emitted output")) .option("silent", desc("Silent logs")) .option("silent", desc("Run in silent mode")) }, argv => { build({ cwd: process.cwd(), config: argv.config, print: argv.print, watch: argv.watch, verbose: !argv.silent }) }).argv; function desc(description) { return { description } } ================================================ FILE: packages/paperclip-cli-minimal/package.json ================================================ { "name": "@paperclip-ui/cli-minimal", "version": "18.5.6", "description": "Minimal command line tool for Paperclip", "main": "lib/cli.js", "publishConfig": { "access": "public" }, "bin": { "paperclip-minimal": "./bin/paperclip" }, "scripts": { "build": "tsc", "build:watch": "tsc --watch --preserveWatchOutput", "lint": "eslint src" }, "author": "", "license": "MIT", "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a", "dependencies": { "@paperclip-ui/builder": "^18.5.6", "@paperclip-ui/cli-utils": "^18.5.6", "@paperclip-ui/core": "^18.5.6", "@paperclip-ui/utils": "^18.5.6", "chokidar": "^3.3.1", "fs-extra": "^9.0.1", "glob": "^7.1.6", "resolve": "^1.15.1", "yargs": "^15.1.0" }, "devDependencies": { "@types/fs-extra": "^9.0.1", "@typescript-eslint/eslint-plugin": "^3.6.1", "@typescript-eslint/parser": "^3.6.1", "eslint": "^7.4.0", "mocha": "^8.0.1", "typescript": "^4.2.3" } } ================================================ FILE: packages/paperclip-cli-minimal/src/build.ts ================================================ import * as resolve from "resolve"; import { buildDirectory, TargetNotFoundError, DirectoryBuilder } from "@paperclip-ui/builder"; import * as path from "path"; import * as URL from "url"; import * as fs from "fs"; import { getPrettyMessage } from "@paperclip-ui/cli-utils"; import { isPaperclipFile, PaperclipConfig, stripFileProtocol } from "@paperclip-ui/utils"; import { createEngineDelegate } from "@paperclip-ui/core"; import { mkdirpSync } from "fs-extra"; export type BuildOptions = { cwd: string; config?: string; print: boolean; watch: boolean; compilerName?: string; sourceDirectory?: string; verbose: boolean; }; export const build = async (options: BuildOptions) => { const config = loadConfig(options); const engine = createEngineDelegate({}); let builder: DirectoryBuilder; try { builder = buildDirectory( { watch: options.watch, cwd: options.cwd, config }, engine ); } catch (error) { if (error instanceof TargetNotFoundError) { console.error(error.message); } else { console.error(error); } return; } builder .onFile((outFilePath: string, content: string) => { if (options.verbose) { console.log("Write %s", path.relative(options.cwd, outFilePath)); } if (options.print && isPaperclipFile(outFilePath)) { console.log(content); } else { writeFileSync(outFilePath, content, options); } }) .onError((error, filePath) => { const info = (error as any).info || error; if (info?.range) { console.error( getPrettyMessage( info, fs.readFileSync(stripFileProtocol(filePath), "utf8"), URL.pathToFileURL(filePath), options.cwd ) ); } else { console.error(error); } }) .start(); }; const writeFileSync = ( filePath: string, content: string, options: BuildOptions ) => { mkdirpSync(path.dirname(filePath)); fs.writeFileSync(filePath, content); }; const loadConfig = (options: BuildOptions): PaperclipConfig => { try { return require(resolve2( options.config || path.join(options.cwd, "/paperclip.config") )); // eslint-disable-next-line } catch (e) { return {}; } }; const resolve2 = module => { try { return resolve.sync(module, { basedir: process.cwd() }); } catch (e) { try { return require.resolve(module); } catch (e) { return null; } } }; ================================================ FILE: packages/paperclip-cli-minimal/src/cli.ts ================================================ export * from "./build"; ================================================ FILE: packages/paperclip-cli-minimal/src/tm.d.ts ================================================ declare module "plist" { export function build(json: any): string; } declare interface MapLike { [s: string]: T; } declare interface TmGrammarRuleName { name: string; } declare interface TmGrammarRule { name?: string; } declare interface TmGrammarMatchRule extends TmGrammarRule { match: string; captures: MapLike; } declare interface TmGrammarBeginEndRule extends TmGrammarRule { contentName?: string; begin: string; end: string; beginCaptures?: MapLike; endCaptures?: MapLike; patterns: AnyTmGrammarRule[]; } declare interface TmGrammarIncludeRule extends TmGrammarRule { include: string; } declare type AnyTmGrammarRule = | TmGrammarMatchRule | TmGrammarBeginEndRule | TmGrammarIncludeRule; declare interface TmGrammarRepositoryPatterns { patterns: AnyTmGrammarRule[]; } declare type TmGrammarRepositaryRule = | AnyTmGrammarRule | TmGrammarRepositoryPatterns; declare interface TmGrammar { name: string; scopeName: string; fileTypes: string[]; uuid: string; variables?: MapLike; patterns?: AnyTmGrammarRule[]; repository: MapLike; } declare interface TmThemeSetting { scope: string; settings: { vsclassificationtype: string }; } declare interface TmTheme { name: string; uuid: string; settings: TmThemeSetting[]; } ================================================ FILE: packages/paperclip-cli-minimal/tsconfig.json ================================================ { "compilerOptions": { "outDir": "lib", "target": "es5", "module": "commonjs", "esModuleInterop": true, "jsx": "react" }, "exclude": [ "node_modules", "*-test", "lib", "index.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-cli-utils/.eslintrc.json ================================================ { "root": true, "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], "rules": { "no-constant-condition": 0, "@typescript-eslint/no-var-requires": 0, "@typescript-eslint/no-empty-function": 0 } } ================================================ FILE: packages/paperclip-cli-utils/.npmignore ================================================ ================================================ FILE: packages/paperclip-cli-utils/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/paperclip-cli-utils/README.md ================================================ CLI tool for compiling paperclip templates. Installation: `npm install @paperclip-ui/cli --save-dev` Documentation: http://paperclip.dev/docs/usage-cli ================================================ FILE: packages/paperclip-cli-utils/bin/paperclip ================================================ #!/usr/bin/env node const {build, init, devStart} = require("../lib/cli"); const argv = require("yargs") .command("build [file-pattern]", "Compiles paperclip files to target language", yargs => { yargs .option("watch", desc("watch for changes")) .option("drop-pc-extension", desc("Drops PC extension from compiled files")) .option("write", desc("Write compiled file")) .option("compiler", desc("Language compiler target (default is from paperclip.config.json if found)")) .option("definition", desc("Write typed definition type")) }, argv => { build({ config: argv.config, write: argv.write, watch: argv.watch, definition: argv.definition, dropPcExtension: argv.dropPcExtension, compilerName: argv.compiler, filePatterns: argv.filePattern }) }) .command("init", "Initialize new paperclip project", yargs => { }, argv => { init(); }) .command("dev", "Starts the dev server", yargs => { yargs .option("port", desc("HTTP port to listen on")) .default("port", 4000) }, argv => { devStart({ port: argv.port, cwd: process.cwd() }); }) .argv; function desc(description) { return { description } } ================================================ FILE: packages/paperclip-cli-utils/package.json ================================================ { "name": "@paperclip-ui/cli-utils", "version": "18.5.6", "description": "CLI utils for Paperclip", "main": "lib/index.js", "publishConfig": { "access": "public" }, "scripts": { "build": "tsc", "build:watch": "tsc --watch --preserveWatchOutput", "lint": "eslint src" }, "author": "", "license": "MIT", "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a", "dependencies": { "@paperclip-ui/core": "^18.5.6", "@paperclip-ui/utils": "^18.5.6", "@types/inquirer": "^6.5.0", "@types/shelljs": "^0.8.8", "chalk": "^4.1.0", "chokidar": "^3.3.1", "dedent": "^0.7.0", "fs-extra": "^9.0.1", "glob": "^7.1.6", "inquirer": "^7.3.0", "resolve": "^1.15.1", "shelljs": "^0.8.4", "yargs": "^15.1.0" }, "devDependencies": { "@types/chalk": "^2.2.0", "@types/fs-extra": "^9.0.1", "@typescript-eslint/eslint-plugin": "^3.6.1", "@typescript-eslint/parser": "^3.6.1", "eslint": "^7.4.0", "lodash": "^4.17.19", "mocha": "^8.0.1", "typescript": "^4.2.3" } } ================================================ FILE: packages/paperclip-cli-utils/src/index.ts ================================================ export * from "./pretty-message"; ================================================ FILE: packages/paperclip-cli-utils/src/pretty-message.ts ================================================ import * as path from "path"; import url from "url"; import { StringRange } from "@paperclip-ui/utils"; import chalk from "chalk"; import dedent from "dedent"; type Details = { range: StringRange; message: string; }; export const getPrettyMessageFromError = (e, code, uri, cwd) => { const info = e.info || e; return info.range ? getPrettyMessage(info, code, uri, cwd) : null; }; export const getPrettyMessage = ( { range, message }: Details, code: string, uri: string, cwd: string ) => { const lines = code.split("\n"); const chunk = lines.slice(range.start.line - 1, range.end.line); const highlightedLines = chunk.reduce((highlight, line, index) => { highlight.push(addLineNumber(line, range.start.line + index, lines.length)); if (index === 0) { let underscoreLength; // same line if (range.end.line === range.start.line) { underscoreLength = range.end.column - (range.start.column - 1); } else { underscoreLength = line.length - range.start.column + 2; } highlight.push( addLineNumberPadding( " ".repeat(range.start.column - 1) + underline(underscoreLength), lines.length ) ); } // make sure that end is on a new line if (range.start.line !== range.end.line && index !== 0) { // in-between lines if (range.start.line + index < range.end.line && line.trim()) { highlight.push( addLineNumberPadding(underline(line.length), lines.length) ); // end of line } else if (range.start.line + index === range.end.line) { highlight.push( addLineNumberPadding(underline(range.end.column - 1), lines.length) ); } } return highlight; }, []); const buffer = dedent` \n ${chalk.cyan(path.relative(cwd, url.fileURLToPath(uri)))}:${chalk.yellow( range.start.line + ":" + range.start.column )} - ${chalk.red("error")}: ${message}\n\n ` + highlightedLines.join("\n") + "\n"; return buffer; }; const underline = (length: number) => chalk.red("^".repeat(length)); const addLineNumber = ( line: string, number: number, high: number, colorize = true ) => { const prefix = `${number}` + " ".repeat(String(high).length + 2 - String(number).length) + "| "; return (colorize ? chalk.grey(prefix) : prefix) + line; }; const addLineNumberPadding = (line: string, high: number) => { return " ".repeat(addLineNumber("", 0, high, false).length) + line; }; ================================================ FILE: packages/paperclip-cli-utils/tsconfig.json ================================================ { "compilerOptions": { "outDir": "lib", "target": "es5", "module": "commonjs", "esModuleInterop": true, "jsx": "react" }, "exclude": [ "node_modules", "*-test", "lib", "index.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-common/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/paperclip-common/index.d.ts ================================================ export * from "./lib"; ================================================ FILE: packages/paperclip-common/index.js ================================================ module.exports = require("./lib"); ================================================ FILE: packages/paperclip-common/package.json ================================================ { "name": "@paperclip-ui/common", "version": "18.5.3", "description": "", "main": "index.js", "scripts": { "build": "tsc", "build:watch": "tsc --watch --preserveWatchOutput" }, "publishConfig": { "access": "public" }, "author": "", "license": "ISC", "dependencies": { "@msgpack/msgpack": "^2.7.1", "fs-extra": "^10.0.0", "immer": "^9.0.5", "lodash": "^4.17.21" }, "devDependencies": { "typescript": "^4.2.3" }, "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a" } ================================================ FILE: packages/paperclip-common/src/bindable/index.ts ================================================ class PropertyBinding< TShape extends Object, TKey extends keyof TShape, TValue = TShape[TKey] > { private _oldValue: any; constructor( private _object: BindableObject, private _name: TKey, private _listener: (newValue: TValue, oldValue: TValue) => void ) { this._trigger(); } private _trigger() { const newValue = this._object.properties[this._name] as any; if (newValue !== this._oldValue) { this._oldValue = newValue; this._listener(newValue, this._oldValue); } } } export class BindableObject { constructor(private _properties: TShape) {} get properties() { return this._properties; } setProperties(properties: Partial) { this._properties = Object.freeze({ ...this._properties, ...properties }); } bindProperty( name: TKey, listener: (value: TShape[TKey], oldValue: TShape[TKey]) => void ) { return new PropertyBinding(this, name, listener); } } ================================================ FILE: packages/paperclip-common/src/disposable.ts ================================================ export type Disposable = { dispose: () => void; }; export const disposableGroup = (disposables: Disposable[]) => ({ dispose() { for (const disposable of disposables) { disposable.dispose(); } } }); ================================================ FILE: packages/paperclip-common/src/em.ts ================================================ export const createListener = ( em: any, type: string, listener: (...args: any[]) => void ) => { em.on(type, listener); return () => em.off(type, listener); }; ================================================ FILE: packages/paperclip-common/src/events.ts ================================================ import { Disposable } from "./disposable"; export type BaseEvent = { type: string; }; export type Dispatcher = (event: BaseEvent) => void; type EventHandler = (event: BaseEvent) => void; export type Observer = { handleEvent: EventHandler; }; class ObservablePipe implements Observer { constructor(private _dest: Observable) {} handleEvent(event: BaseEvent) { this._dest.dispatch(event); } } export class Observable { private _observers: Observer[]; constructor() { this._observers = []; } dispatch(event: BaseEvent) { for (let i = this._observers.length; i--; ) { this._observers[i].handleEvent(event); } } pipe(observable: Observable) { return this.observe(new ObservablePipe(observable)); } source(observable: Observable) { return observable.pipe(this); } observe(observer: Observer): Disposable { if (!observer.handleEvent) { throw new Error(`handleEvent not defined`); } this._observers.push(observer); return { dispose() { this.unobserve(observer); } }; } unobserve(observer: Observer) { const index = this._observers.indexOf(observer); if (index !== -1) { this._observers.splice(index, 1); } } } export type EventProcessCreator = ( event: BaseEvent ) => Disposable | Promise; export const eventHandler = ( type: string, handler: (event: BaseEvent) => void ) => (event: BaseEvent) => { if (event.type === type) { handler(event); } }; export const eventHandlers = ( handlers: Record void> ) => (event: BaseEvent) => { if (handlers[event.type]) { handlers[event.type](event); } }; export const eventProcesses = ( handlers: Record ): EventHandler => { const processes: Record> = {}; return async (event: BaseEvent) => { const handleEvent = handlers[event.type]; if (handleEvent) { // NOTE - this is problematic if other events come in - we want to use // something like take latest if (processes[event.type]) { (await processes[event.type]).dispose(); } processes[event.type] = handleEvent(event); } }; }; type EventCreator = () => TEvent; ================================================ FILE: packages/paperclip-common/src/immutable-store.ts ================================================ import { EventEmitter } from "events"; import { produce } from "immer"; import { isEqual } from "lodash"; export class ImmutableStore { private _state: TState; private _em: EventEmitter; constructor(initialState: TState, bind?: (state: TState) => void) { this._state = { ...initialState }; this._em = new EventEmitter(); if (bind) { this.bind(bind); } } getState() { return this._state; } bind( listener: (state: TSelectedValue) => void, select: (state: TState) => TSelectedValue = identity ) { listener(select(this.getState())); return this.onChange(listener, select); } onChange( listener: (newState: TSelectedValue, oldState: TSelectedValue) => void, select: (state: TState) => TSelectedValue = identity ) { const onChange = (newState: TState, oldState: TState) => { const newValue = select(newState); const oldValue = select(oldState); if (!isEqual(newValue, oldValue)) { listener(newValue, oldValue); } }; this._em.on("change", onChange); return () => this._em.off("change", onChange); } update(updater: (state: TState) => void) { const newState = produce(this._state, updater); if (this._state !== newState) { const oldState = this._state; this._state = newState; this._em.emit("change", newState, oldState); } } } const identity = (v) => v; ================================================ FILE: packages/paperclip-common/src/index.ts ================================================ export * from "./immutable-store"; export * from "./disposable"; export * from "./events"; export * from "./events"; export * from "./services"; export * from "./kernel"; export * from "./rpc"; export * from "./em"; export * from "./log"; export * from "./promise"; ================================================ FILE: packages/paperclip-common/src/kernel.ts ================================================ import { BaseEvent, Observable } from "./events"; export interface BaseServerKernel { readonly events: Observable; } export class ServerKernel implements BaseServerKernel { events = new Observable(); } ================================================ FILE: packages/paperclip-common/src/log.ts ================================================ export enum LogLevel { Verbose = 1, Info = 1 << 1, Warn = 1 << 2, Error = 1 << 3, All = LogLevel.Verbose | LogLevel.Info | LogLevel.Warn | LogLevel.Error, None = 0, } export class Logger { constructor(readonly logLevel: LogLevel = LogLevel.All) {} info(...args) { if (this.logLevel & LogLevel.Info) { console.info(...args); } } warn(...args) { if (this.logLevel & LogLevel.Warn) { console.warn(...args); } } error(...args) { if (this.logLevel & LogLevel.Error) { console.error(...args); } } verbose(...args) { if (this.logLevel & LogLevel.Verbose) { console.log(...args); } } } ================================================ FILE: packages/paperclip-common/src/promise.ts ================================================ export class Deferred { private _promise: Promise; private _resolve: (value: TType) => void; private _reject: (any) => void; constructor() { this._promise = new Promise((resolve, reject) => { this._resolve = resolve; this._reject = reject; }); } get promise() { return this._promise; } get resolve() { return this._resolve; } get reject() { return this._reject; } } export const deferPromise = (): [ Promise, (value: TValue) => void ] => { let _resolve; const promise = new Promise((resolve) => { _resolve = resolve; }); return [promise, _resolve]; }; ================================================ FILE: packages/paperclip-common/src/remote-channel.ts ================================================ // import { Disposable } from "./disposable"; // export type Channel = { // call: (request: TRequest) => Promise; // listen: (handle: (request: TRequest) => Promise) => Disposable; // }; // const spy = (obj, prop, handler) => { // const oldProp = obj[prop]; // let spyFn; // // spy exists? Use that // if (oldProp?.callbacks) { // spyFn = oldProp; // } else { // const callbacks = []; // spyFn = (...args) => { // for (const cb of callbacks) { // cb && cb(...args); // } // }; // spyFn.callbacks = callbacks; // } // spyFn.callbacks.push(handler); // obj[prop] = spyFn; // return () => { // if (spyFn.callbacks.length === 1) { // obj[prop] = spyFn[spyFn.callbacks[0]]; // } else { // const i = spyFn.callbacks.indexOf(handler); // if (i !== -1) { // spyFn.callbacks.splice(i, 1); // } // } // }; // }; // type Message = { // name: string; // channelId: any; // id: any; // payload?: any; // error?: any; // }; // type Adapter = { // onMessage: (listener: (message: Message) => void) => () => void; // send: (message: Message) => void; // }; // export const workerRPCClientAdapter = (worker: Window | Worker): Adapter => ({ // onMessage(listener) { // return spy(worker, "onmessage", event => listener(event.data)); // }, // send(message) { // (worker as any).postMessage(message); // } // }); // // sockjs adapter // export const sockjsClientAdapter = (worker: any): Adapter => ({ // onMessage(listener) { // // is on the server // const onMessage = message => { // listener(JSON.parse(message)); // }; // // is on the client // if (!worker.on) { // return spy(worker, "onmessage", event => { // onMessage(event.data); // }); // } // worker.on("data", onMessage); // return () => worker.off("data", onMessage); // }, // send(message) { // ((worker as any).send || (worker as any).write).call( // worker, // JSON.stringify(message) // ); // } // }); // export const remoteChannel = (name: string) => { // const requestName = `${name}:request`; // const responseName = `${name}:response`; // return (chan: Adapter): Channel => { // const channelId = Math.random(); // const call = (payload: any): Promise => { // let id = Math.random(); // return new Promise((resolve, reject) => { // const onMessage = message => { // if (message.id === id) { // disposeListener(); // if (message.error) { // reject(message.error); // } else { // resolve(message.payload); // } // } // }; // const disposeListener = chan.onMessage(onMessage); // chan.send({ name: requestName, payload, id, channelId }); // }); // }; // const listen = (call: (payload: any) => Promise) => { // const dispose = chan.onMessage(async message => { // if (message.name === requestName && message.channelId !== channelId) { // try { // chan.send({ // name: responseName, // id: message.id, // channelId, // payload: await call(message.payload) // }); // } catch (error) { // chan.send({ // name: responseName, // id: message.id, // channelId, // error // }); // } // } // }); // return { dispose }; // }; // return { call, listen }; // }; // }; ================================================ FILE: packages/paperclip-common/src/rpc/adapters.ts ================================================ import { spy } from "./spy"; import { EventEmitter } from "events"; import * as sockjs from "sockjs"; import { WebSocketServer } from "ws"; import * as mpack from "@msgpack/msgpack"; type Message = any; export type RPCServer = { onConnection(listener: (client: RPCClientAdapter) => void); }; export type RPCClientAdapter = { onMessage: (listener: (message: Message) => void) => () => void; onDisconnect: (listener: () => void) => void; send: (message: Message) => void; }; export const createMockRPCServer = () => { const hostEm = new EventEmitter(); hostEm.setMaxListeners(30); return { onConnection(listener) { hostEm.on("connection", listener); }, createConnection(delay?: boolean): RPCClientAdapter { const remote = new EventEmitter(); const local = new EventEmitter(); remote.setMaxListeners(30); local.setMaxListeners(30); const remoteCon = createMockClient(delay, local, remote); const localCon = createMockClient(delay, remote, local); hostEm.emit("connection", localCon); return remoteCon; }, }; }; const createMockClient = ( delay: boolean, a: EventEmitter, b: EventEmitter ): RPCClientAdapter => { return { send(message) { const emitNow = () => a.emit("message", message); if (delay) { setTimeout(emitNow, 0); } else { emitNow(); } }, onMessage(listener) { const listener2 = (v) => { listener(v); }; b.on("message", listener2); return () => { b.off("message", listener2); }; }, onDisconnect() {}, }; }; export const workerRPCClientAdapter = ( worker: Window | Worker ): RPCClientAdapter => ({ onMessage(listener) { return spy(worker, "onmessage", (event) => listener(event.data)); }, onDisconnect(listener: () => void) {}, send(message) { (worker as any).postMessage(message); }, }); // sockjs adapter export const sockjsClientAdapter = (worker: any): RPCClientAdapter => { let prebuff = []; const send = (message) => { if (prebuff) { prebuff.push(message); return; } try { ((worker as any).send || (worker as any).write).call(worker, message); } catch (e) { console.error(e.stack); throw e; } }; const onOpen = () => { const buffer = prebuff; prebuff = undefined; for (const message of buffer) { send(message); } }; if (!worker.on) { worker.onopen = onOpen; } else { onOpen(); } return { onMessage(listener) { // is on the server const onMessage = (message) => { listener(message); }; // is on the client if (!worker.on) { return spy(worker, "onmessage", (event) => { onMessage(event.data); }); } worker.on("data", onMessage); return () => worker.off("data", onMessage); }, onDisconnect(listener: () => void) { worker.on("disconnect", listener); }, send, }; }; // sockjs adapter export const sockjsServerRPCAdapter = (server: sockjs.Server): RPCServer => ({ onConnection(listener: (connection: RPCClientAdapter) => void) { server.on("connection", (connection) => { listener(sockjsClientAdapter(connection)); }); }, }); export const wsAdapter = ( createWS: () => any, isOpen = false ): RPCClientAdapter => { let buffer; let ws; const em = new EventEmitter(); em.setMaxListeners(30); const init = () => { if (!isOpen) { buffer = []; } ws = createWS(); ws.binaryType = "arraybuffer"; if (ws.on) { ws.on("open", em.emit.bind(em, "open")); ws.on("message", (message) => { em.emit("message", mpack.decode(message)); }); ws.on("close", em.emit.bind(em, "close")); } else { ws.onopen = () => em.emit("open"); ws.onmessage = (event) => em.emit("message", mpack.decode(event.data)); ws.onclose = () => em.emit("close"); } }; init(); if (!isOpen) { em.on("close", init); } const send = (message) => { if (buffer) { return buffer.push(message); } ws.send(mpack.encode(message)); }; em.on("open", () => { const buff = buffer; buffer = null; for (const item of buff) { send(item); } }); return { onDisconnect(listener: () => void) {}, onMessage(listener: (message: any) => void) { em.on("message", listener); return () => em.off("message", listener); }, send, }; }; export const wsServerAdapter = (wss: WebSocketServer): RPCServer => ({ onConnection(listener: (connection: RPCClientAdapter) => void) { wss.on("connection", (ws) => { listener(wsAdapter(() => ws, true)); }); }, }); ================================================ FILE: packages/paperclip-common/src/rpc/chan.ts ================================================ import { RPCClientAdapter } from "./adapters"; export type Channel = { call: (request: TRequest) => Promise; listen: (handle: (request: TRequest) => Promise) => () => void; }; export const remoteChannel = (name: string) => { const requestName = `${name}:request`; const responseName = `${name}:response`; return (chan: RPCClientAdapter): Channel => { const channelId = Math.random(); const call = (payload: any): Promise => { let id = Math.random(); return new Promise((resolve, reject) => { const onMessage = (message) => { if (message.id === id) { disposeListener(); if (message.error) { reject(message.error); } else { resolve(message.payload); } } }; const disposeListener = chan.onMessage(onMessage); chan.send({ name: requestName, payload, id, channelId }); }); }; const listen = (call: (payload: any) => Promise) => { const dispose = chan.onMessage(async (message) => { if (message.name === requestName && message.channelId !== channelId) { try { chan.send({ name: responseName, id: message.id, channelId, payload: await call(message.payload), }); } catch (error) { chan.send({ name: responseName, id: message.id, channelId, error: error.stack, }); } } }); return dispose; }; return { call, listen }; }; }; ================================================ FILE: packages/paperclip-common/src/rpc/index.ts ================================================ export * from "./chan"; export * from "./adapters"; ================================================ FILE: packages/paperclip-common/src/rpc/spy.ts ================================================ export const spy = (obj, prop, handler) => { const oldProp = obj[prop]; let spyFn; // spy exists? Use that if (oldProp?.callbacks) { spyFn = oldProp; } else { const callbacks = []; spyFn = (...args) => { for (const cb of callbacks) { cb && cb(...args); } }; spyFn.callbacks = callbacks; } spyFn.callbacks.push(handler); obj[prop] = spyFn; return () => { if (spyFn.callbacks.length === 1) { obj[prop] = spyFn[spyFn.callbacks[0]]; } else { const i = spyFn.callbacks.indexOf(handler); if (i !== -1) { spyFn.callbacks.splice(i, 1); } } }; }; ================================================ FILE: packages/paperclip-common/src/services.ts ================================================ import { BaseEvent, Observer } from "./events"; import { BaseServerKernel } from "./kernel"; export class ServiceInitialized implements BaseEvent { static TYPE = "ServiceEvent/INITIALIZED"; readonly type = ServiceInitialized.TYPE; } export interface BaseServerState extends Observer {} export const serviceCreator = ( load: (kernel: TKernel, state: TState) => void, createState?: () => TState ) => (kernel: TKernel) => { let state: TState; if (createState) { state = createState(); if (((state as any) as Observer).handleEvent) { kernel.events.observe((state as any) as Observer); } } load(kernel, state); }; export class ServiceManager { constructor(private _kernal: TKernel) {} add(...serviceCreators: Array<(kernel: TKernel) => void>) { serviceCreators.forEach(createService => { createService(this._kernal); }); return this; } initialize() { this._kernal.events.dispatch(new ServiceInitialized()); } } ================================================ FILE: packages/paperclip-common/src/string-editor.ts ================================================ ================================================ FILE: packages/paperclip-common/src/test-utils/index.ts ================================================ export * from "./tmp-fixtures"; ================================================ FILE: packages/paperclip-common/src/test-utils/tmp-fixtures.ts ================================================ import * as path from "path"; import * as URL from "url"; import * as fsa from "fs-extra"; import { kebabCase } from "lodash"; export const saveTmpFixtureFiles = ( title: string, files: Record, basedir: string ) => { const testDir = path.join(basedir, kebabCase(title)); const fixtureUris: Record = {}; const saveFiles = (files: Record) => { for (const relativePath in files) { const filePath = path.join(testDir, relativePath); fixtureUris[relativePath] = URL.pathToFileURL(filePath).href; fsa.mkdirpSync(path.dirname(filePath)); fsa.writeFileSync(filePath, files[relativePath]); } }; saveFiles(files); return { testDir, fixtureUris, saveFiles, dispose: () => { fsa.rmSync(testDir, { recursive: true }); }, }; }; ================================================ FILE: packages/paperclip-common/tsconfig.json ================================================ { "compilerOptions": { "outDir": "lib", "target": "es5", "declaration": true, "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true }, "exclude": ["node_modules", "*-test", "lib", "index.d.ts", "test"], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-compiler-base-jsx/.eslintrc.json ================================================ { "root": true, "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], "rules": { "no-constant-condition": 0, "@typescript-eslint/explicit-module-boundary-types": 0, "@typescript-eslint/no-unused-vars": 2, "@typescript-eslint/no-var-requires": 0, "no-undef": 0 } } ================================================ FILE: packages/paperclip-compiler-base-jsx/package.json ================================================ { "name": "@paperclip-ui/compiler-base-jsx", "version": "18.5.6", "description": "HTML compiler for Paperclip", "main": "lib/index.js", "scripts": { "lint": "eslint src", "test": "mocha lib/test", "build": "tsc", "build:watch": "tsc --watch" }, "publishConfig": { "access": "public" }, "author": "", "license": "ISC", "devDependencies": { "@types/mocha": "^8.0.0", "@typescript-eslint/eslint-plugin": "^3.6.1", "@typescript-eslint/parser": "^3.6.1", "chai": "^4.2.0", "eslint": "^7.4.0", "mocha": "^8.0.1" }, "dependencies": { "@babel/core": "^7.16.7", "@babel/preset-env": "^7.16.7", "@paperclip-ui/interim": "^18.5.6", "@paperclip-ui/utils": "^18.5.6", "lodash": "^4.17.21", "source-map": "^0.7.3" } } ================================================ FILE: packages/paperclip-compiler-base-jsx/src/code-compiler.ts ================================================ import { InterimNode, InterimText, InterimElement, InterimNodeKind, InterimSlotNode, InterimComponent, InterimModule, StaticAttributeValuePart, InterimAttributeValuePart, InterimConjunctionOperator, DynamicAttributeValuePart, InterimAttributeValuePartKind, ShorthandAttributeValuePart, InterimAttributeValue } from "@paperclip-ui/interim"; import { camelCase, omit } from "lodash"; import { InterimScriptExpressionKind, InterimScriptExpression } from "@paperclip-ui/interim"; import { arrayJoin, createTranslateContext, getElementInstanceName, startBlock, endBlock, addBuffer, writeSourceNode, writeJoin, ContextWriter } from "./utils"; import { Html5Entities } from "html-entities"; import { Context } from "./utils"; import { SourceNode } from "source-map"; import { CompilerOptions, PaperclipConfig } from "@paperclip-ui/utils"; const entities = new Html5Entities(); const UTILS = ` function getDefault(module) { return module.default || module; } function castStyle(value) { var tov = typeof value; if (tov === "object" || tov !== "string" || !value) return value; return value.trim().split(";").reduce(function (obj, keyValue) { var kvp = keyValue.split(":"); var key = kvp[0]; var value = kvp[1]; if (!value || value === "undefined") return obj; var trimmedValue = value.trim(); if (trimmedValue === "undefined" || !trimmedValue) return obj; obj[key.trim()] = trimmedValue; return obj; }, {}); }`.trim(); export type CodeCompilerOptions = { imports?: string; preflight: string; vendorName: string; }; export const codeCompiler = ({ preflight, imports = "", vendorName }: CodeCompilerOptions) => { const translateExportedStyles = addBuffer([ `export const classNames = {\n`, startBlock, (context: Context) => writeJoin( Object.keys(context.module.css.exports.classNames), ",\n", key => addBuffer([ JSON.stringify(key), ": ", JSON.stringify(context.module.css.exports.classNames[key]) ]) )(context), endBlock, "\n", "};" ]); const translateImports = (context: Context) => { return context.module.imports.reduce((context, imp) => { context = addBuffer([`import `, `_${camelCase(imp.publicScopeId)}`])( context ); if (imp.usedTagNames.length) { const parts = []; for (const tagName of imp.usedTagNames) { parts.push( tagName + " as " + getElementInstanceName(imp.namespace, tagName, context) ); } context = addBuffer([`, {`, arrayJoin(parts, ","), `}`])(context); } context = addBuffer([` from "${imp.relativePath}";`, "\n"])(context); return context; }, context); }; const compileComponents = (context: Context) => writeJoin(context.module.components, "\n", compileComponent)(context); const compileComponent = (component: InterimComponent) => { const tagName = component.as === "default" ? "$$Default" : component.as; return writeSourceNode( component.range.start, addBuffer([ `var ${tagName} = ${vendorName}.memo(${vendorName}.forwardRef(function ${tagName}(props, ref) {`, "\n", startBlock, "return ", compileElement(component), endBlock, "\n", "}));\n", context => { if (!component.exported) { return context; } if (component.as === "default") { return addBuffer([`export default ${tagName};\n`])(context); } else { return addBuffer([`export { ${component.as} };\n`])(context); } } ]) ); }; const compileElement = (element: InterimElement | InterimComponent) => writeSourceNode(element.range.start, context => { context = addBuffer([`${vendorName}.createElement(`])(context); let tagName; if (element.tagName === "fragment") { tagName = `${vendorName}.Fragment`; } else { tagName = nativeOrInstanceTag(element, context); } if (element.attributes.tagName && !element.isInstance) { const tagNameStr = tagName; tagName = context => addBuffer(["props.tagName || ", tagNameStr])(context); } return addBuffer([ tagName, ", ", compileAttributes(element), ", ", compileChildren(element.children), ")" ])(context); }); const nativeOrInstanceTag = ( element: InterimElement | InterimComponent, context: Context ) => { if (element.isInstance) { return getElementInstanceName( element.tagName, element.innerTagName, context ); } return `"${element.tagName}"`; }; const compileAttributes = (element: InterimElement | InterimComponent) => ( context: Context ) => { context = addBuffer(["{\n", startBlock])(context); context = writeClassAttribute(element)(context); const omitAttributeNames = ["class"]; if (!element.isInstance) { omitAttributeNames.push("tagName"); } const attrKeys = Object.keys(omit(element.attributes, omitAttributeNames)); if (element.kind === InterimNodeKind.Component) { context = addBuffer([`"ref": ref,\n`])(context); } // context = addBuffer([`"key": "${element.id}",\n`])(context); context = writeJoin( attrKeys, ",\n", key => addBuffer([ addBuffer([JSON.stringify(key), ": "]), compileAttributeValue( element, key, element.attributes[key].variants, () => write => write ) ]), true )(context); return addBuffer([endBlock, "}"])(context); }; const writeClassAttribute = (element: InterimElement | InterimComponent) => ( context: Context ) => { let writeClassValue = addBuffer([ `"${element.scopeClassNames.join(" ")}"`, element.attributes.class && addBuffer([ ` + `, compileAttributeValue( element, "class", element.attributes.class.variants, conditional => conditional ? compileConditionalClassNamePart : compileEssentialClassNamePart ) ]) ]); if (element.tagName === "fragment") { if (element.attributes.tagName) { const inner = writeClassValue; writeClassValue = addBuffer(["props.tagName", " ? ", inner, " : null"]); } else { return context; } } return addBuffer([ `${element.isInstance ? '"class"' : '"className"'}: `, writeClassValue, ",\n" ])(context); }; const compileConditionalClassNamePart = (inner: ContextWriter) => addBuffer(["(", inner, " ? ", `" " + `, inner, ` : `, `""`, ")"]); const compileEssentialClassNamePart = (inner: ContextWriter) => addBuffer([`" " + `, inner]); const compileAttributeValue = ( element: InterimElement | InterimComponent, attrName: string, variants: InterimAttributeValue[], outer: (conditional: boolean) => (inner: ContextWriter) => ContextWriter ) => writeJoin(variants, ` + `, variant => context => { if (!variant.parts) { return addBuffer([`true`])(context); } if (variant.variantName) { context = addBuffer([ "(", prop(variant.variantName), " ? ", compileVariantParts(element, attrName, variant.parts, outer), ` : "")` ])(context); } else { context = compileVariantParts( element, attrName, variant.parts, outer )(context); } return context; }); const compileVariantParts = ( element: InterimElement | InterimComponent, attrName: string, parts: InterimAttributeValuePart[], outer: (conditional: boolean) => (inner: ContextWriter) => ContextWriter ) => (context: Context) => { let write = writeJoin( parts, " + ", compileAttributeValuePart(attrName, outer) ); if (attrName === "style") { write = addBuffer([`castStyle(`, write, `)`]); } if ( attrName === "src" && !element.isInstance && context.targetOptions.importAssetsAsModules === true && parts.length === 1 && parts[0].kind === InterimAttributeValuePartKind.Static ) { write = addBuffer([`getDefault(require(`, write, `))`]); } return write(context); }; const prop = (name: string) => `props["${name}"]`; const compileAttributeValuePart = ( name: string, format: (conditional: boolean) => (write: ContextWriter) => ContextWriter ) => (part: InterimAttributeValuePart) => { switch (part.kind) { case InterimAttributeValuePartKind.Dynamic: return compileDynamicAttributePart(part, format); case InterimAttributeValuePartKind.Static: return compileStaticAttributePart(part, format); case InterimAttributeValuePartKind.Shorthand: return compileShorthandAttributePart(name, part, format); } }; const compileDynamicAttributePart = ( part: DynamicAttributeValuePart, outer: (conditional: boolean) => (inner: ContextWriter) => ContextWriter ) => { let write = compileScript(part.script); if (outer) { const prev = write; write = outer(true)(prev); } else { write = addBuffer(["(", write, " || ", '""', ")"]); } return writeSourceNode(part.range?.start, write); }; const CONJ_MAP = { [InterimConjunctionOperator.And]: "&&", [InterimConjunctionOperator.Or]: "||" }; const compileScript = (script: InterimScriptExpression) => writeSourceNode(script.range.start, scriptCompiler(script)); const scriptCompiler = (script: InterimScriptExpression) => { switch (script.kind) { case InterimScriptExpressionKind.String: return addBuffer([JSON.stringify(script.value)]); case InterimScriptExpressionKind.Reference: return addBuffer([prop(script.name)]); case InterimScriptExpressionKind.Number: return addBuffer([String(script.value)]); case InterimScriptExpressionKind.Not: return addBuffer(["!", compileScript(script.expression)]); case InterimScriptExpressionKind.Group: { return addBuffer(["(", compileScript(script.inner), ")"]); } case InterimScriptExpressionKind.Element: return compileElement(script.element); case InterimScriptExpressionKind.Conjunction: { return addBuffer([ compileScript(script.left), CONJ_MAP[script.operator], compileScript(script.right) ]); } case InterimScriptExpressionKind.Boolean: return addBuffer([String(script.value)]); } }; const compileStaticAttributePart = ( part: StaticAttributeValuePart, outer: (conditional: boolean) => (inner: ContextWriter) => ContextWriter ) => writeSourceNode( part.range?.start, outer(false)(addBuffer([JSON.stringify(part.value)])) ); const compileShorthandAttributePart = ( name: string, part: ShorthandAttributeValuePart, outer: (conditional: boolean) => (inner: ContextWriter) => ContextWriter ) => writeSourceNode(part.range?.start, outer(true)(addBuffer([prop(name)]))); const compileText = (text: InterimText) => addBuffer([ writeSourceNode( text.range.start, addBuffer([JSON.stringify(entities.decode(text.value))]) ), "\n" ]); const compileSlot = (slot: InterimSlotNode) => (context: Context) => { return compileScript(slot.script)(context); }; const compileChildren = (children: InterimNode[]) => (context: Context) => { if (children.length === 0) { return addBuffer([`null`])(context); } // context = addBuffer([`[\n`])(context); context = addBuffer(["\n", startBlock])(context); context = writeJoin(children, ",\n", child => { switch (child.kind) { case InterimNodeKind.Element: { return compileElement(child); } case InterimNodeKind.Text: { return compileText(child); } case InterimNodeKind.Slot: { return compileSlot(child); } } })(context); context = addBuffer(["\n", endBlock])(context); return context; // return addBuffer([endBlock, `]`])(context); }; return ( module: InterimModule, filePath: string, config: PaperclipConfig, targetOptions: CompilerOptions, includes: string[] ) => { const context = writeSourceNode( { line: 1, column: 1, pos: 1 }, addBuffer([ imports, translateImports, writeJoin( includes, "\n", include => addBuffer([`import "${include}";`]), true ), preflight, UTILS, "\n\n", translateExportedStyles, "\n\n", compileComponents, "\n\n" ]) )(createTranslateContext(module, filePath, config, targetOptions)); return (context.buffer[0] as SourceNode).toStringWithSourceMap(); }; }; ================================================ FILE: packages/paperclip-compiler-base-jsx/src/definition-compiler.ts ================================================ import { ShapeProperty, InferenceKind, Inference } from "@paperclip-ui/core"; import { createTranslateContext, Context, addBuffer, startBlock, endBlock, writeJoin, } from "./utils"; import { InterimComponent, InterimModule } from "@paperclip-ui/interim"; import { CompilerOptions, PaperclipConfig } from "@paperclip-ui/utils"; export type DefinitionCompilerOptions = { imports: string; elementType: string; }; export const definitionCompiler = ({ imports, elementType, }: DefinitionCompilerOptions) => { const translateRoot = (context: Context) => { context = addBuffer([ `/* eslint-disable */\n`, imports, translateDefaultProps, `type Factory = (props: TProps & DefaultProps) => ${elementType};\n\n`, translateClassNames, translateComponents, ])(context); return context; }; const translateDefaultProps = addBuffer([ `type DefaultProps = {\n`, startBlock, `ref?: any,\n`, endBlock, `};\n\n`, ]); const translateClassNames = (context: Context) => { context = addBuffer([ `export declare const classNames: {\n`, startBlock, (context) => { const classNames = context.module.css.exports.classNames; for (const exportName in classNames) { context = addBuffer([`${JSON.stringify(exportName)}: string,\n`])( context ); } return context; }, endBlock, "};\n\n", ])(context); return context; }; const DEFAULT_PARAM_TYPE = `any`; const translateInference = (property: string, inference: Inference) => { if (inference.kind === InferenceKind.Any) { return addBuffer([ /^on\w+/.test(property) ? `Function` : DEFAULT_PARAM_TYPE, ]); } if (inference.kind === InferenceKind.Array) { return addBuffer([ `Array<`, translateInference(property, inference.value), `>`, ]); } if (inference.kind === InferenceKind.Shape) { return addBuffer([ `{\n`, startBlock, (context) => { for (const key in inference.properties) { context = translateProp(key, inference.properties[key])(context); } return context; }, endBlock, "}", ]); } return context; }; const translateComponents = (context: Context) => writeJoin( context.module.components.filter((component) => component.exported), "\n\n", translateComponent )(context); const translateComponent = (component: InterimComponent) => { const name = component.as === "default" ? "$$Default" : component.as; return addBuffer([ `type ${name}Props = {\n`, startBlock, translateComponentProps(component), endBlock, "}\n\n", `declare const ${name}: Factory<${name}Props>;\n\n`, component.as === "default" ? `export default ${name};` : `export { ${name} };\n\n`, ]); }; const translateComponentProps = (component: InterimComponent) => (context: Context) => { const props = {}; for (const key in component.schema.properties) { context = translateProp(key, component.schema.properties[key])(context); props[key] = [null]; } return context; }; const translateProp = (key: string, { value, optional }: ShapeProperty) => addBuffer([ `${key}${optional ? "?" : ""}: `, translateInference(key, value), ",\n", ]); return ( module: InterimModule, filePath: string, config: PaperclipConfig, targetOptions: CompilerOptions ): string => { const context = createTranslateContext( module, filePath, config, targetOptions ); return translateRoot(context).buffer.join(""); }; }; ================================================ FILE: packages/paperclip-compiler-base-jsx/src/index.ts ================================================ import { CompileOptions, TargetCompiler } from "@paperclip-ui/interim"; import { CompilerOptions } from "@paperclip-ui/utils"; import { codeCompiler, CodeCompilerOptions } from "./code-compiler"; import * as babel from "@babel/core"; import { definitionCompiler, DefinitionCompilerOptions } from "./definition-compiler"; export type CompilersOptions = { code: CodeCompilerOptions; definition: DefinitionCompilerOptions; extensionName: string | ((config: CompilerOptions) => string); }; export const compilers = ({ code, definition, extensionName }: CompilersOptions): TargetCompiler => { const compile2Code = codeCompiler(code); const compile2Defition = definitionCompiler(definition); return ({ module, fileUrl, includes, config, targetOptions }: CompileOptions) => { const { code, map } = compile2Code( module, fileUrl, config, targetOptions, includes ); let outputCode = code; if (targetOptions.es5) { outputCode = babel.transformSync(code, { presets: ["@babel/preset-env"] }) .code; } const ext = typeof extensionName == "function" ? extensionName(targetOptions) : extensionName; return { ["." + ext]: outputCode, ["." + ext + ".map"]: map.toString(), ".d.ts": compile2Defition(module, fileUrl, config, targetOptions) }; }; }; ================================================ FILE: packages/paperclip-compiler-base-jsx/src/test/index.ts ================================================ export * from "./test-suite"; export * from "./utils"; ================================================ FILE: packages/paperclip-compiler-base-jsx/src/test/test-suite.ts ================================================ export const TEST_SUITE = [ [ "can render a simple module", { "/entry.pc": `
    Hello
    ` }, { HelloWorld: {} }, {}, { HelloWorld: `
    Hello
    ` } ], [ "can render various slots", { "/entry.pc": `
    {message}
    ` }, { Entry: { message: "bbb" } }, {}, { Entry: `
    bbb
    ` } ], [ "Can include style from another module", { "/entry.pc": `
    `, "/colors.pc": ` ` }, { Entry: {} }, {}, { Entry: `
    ` } ], [ "Can render a component from within the same document", { "/entry.pc": `
    {children}
    {children}
    ` }, { Entry: { children: "b" } }, {}, { Entry: `
    b
    ` } ], [ "Can render styles with the shorthand prop", { "/entry.pc": `
    ` }, { Entry: { style: { color: "red" } } }, {}, { Entry: `
    ` } ], [ "Can render styles with the long form prop", { "/entry.pc": `
    ` }, { Entry: { style: { color: "red" } } }, {}, { Entry: `
    ` } ], [ "can render style string", { "/entry.pc": `
    ` }, { Entry: { style: "color: red" } }, {}, { Entry: `
    ` } ], [ "can render a dynamic style string", { "/entry.pc": `
    ` }, { Entry: { color: "red" } }, {}, { Entry: `
    ` } ], [ "Can render a dynamic string when the value is undefined", { "/entry.pc": `
    ` }, { Entry: { a1: "red" } }, {}, { Entry: `
    ` } ], [ "Doesn't apply scoped classes when dynamic string applied to component", { "/entry.pc": `
    ` }, { Entry: { className: "ab" } }, {}, { Entry: `
    ` } ], [ "Can change the tag name of a component", { "/entry.pc": `
    ` }, { Entry: { tagName: "span", tagName2: "h1" } }, {}, { Entry: `

    ` } ], [ "Can apply scoped styles to component instance", { "/entry.pc": `
    ` }, { Entry: {} }, {}, { Entry: `
    ` } ], // [ // "Can apply scoped styles to component instance that already has a class", // { // "/entry.pc": ` //
    //
    //
    // // // //
    // ` // }, // { // Entry: {} // }, // { // Entry: `
    ` // } // ], // class names need to be made _explicit_. A DX problem with this is where we're passing // class names within the same doc - the developer needs to prefix with $ in this case. The reason for this // is to ensure that class names _outside_ of the doc aren't accidentally triggering class names within this doc, especially // for JSX. [ "class names applied in the same doc aren't prefixed with scope", { "/entry.pc": `
    ` }, { Entry: {} }, {}, { Entry: `
    ` } ], [ "Can apply scoped styles to a instance of instaance of component", { "/entry.pc": `
    ` }, { Entry: {} }, {}, { Entry: `
    ` } ], [ "Can import elements that are used in slots", { "/entry.pc": `
    {something}
    } /> `, "/button.pc": `
    click me!
    ` }, { Entry: {} }, {}, { Entry: `
    click me!
    ` } ], [ "Can render nodes with &&, !, and ||", { "/entry.pc": `
    {show && A} {!show && B} {false && C} {!false && D} {true && E} {0 && F} {0 && G || H} {false || I} {!!show && J} {(1 || 2) && K}
    ` }, { Entry: { show: true } }, {}, { Entry: `
    ADE0HIJK
    ` } ], [ "Can render && and || in attribute", { "/entry.pc": `
    ` }, { Test: {} }, {}, { Test: `
    ` } ], [ "Maintains attribute casing", { "/entry.pc": `
    ` }, { Entry: {} }, {}, { Entry: `
    ` } ], [ "Cannot change tag name if tagName isn't present", { "/entry.pc": `
    ` }, { Entry: { tagName: "test" } }, {}, { Entry: `
    ` } ], [ "inject-style prop works", { "/entry.pc": `
    `, "/module.pc": ` ` }, { Entry: { tagName: "test" } }, {}, { Entry: `
    ` } ], [ "explicit reference doesn't get mixed with injected scopes", { "/entry.pc": `
    `, "/a.pc": ` `, "/b.pc": ` ` }, { Entry: { tagName: "test" } }, {}, { Entry: `
    ` } ], [ "Can reference injected classes within class variants", { "/entry.pc": `
    `, "/a.pc": ` ` }, { Entry: { test: true } }, {}, { Entry: `
    ` } ], [ "Can render an import within a block", { "/entry.pc": `
    {} {a && } {!b? && }
    `, "/module.pc": `
    Hello
    `, "/a.pc": ` ` }, { Entry: { a: true, b: false } }, {}, { Entry: `
    Hello
    Hello
    Hello
    ` } ], [ "Can define styles on component with prop bound class and no class binding", { "/entry.pc": `
    ` }, { Entry: { a: true, b: false } }, {}, { Entry: `
    ` } ], [ "Can pass propery bound attributes to components", { "/entry.pc": `
    ` }, { Entry: { active: true } }, {}, { Entry: `
    ` } ], [ "Can render a fragment", { "/entry.pc": ` {children}!
    Hello! ` }, { Entry: { active: true } }, {}, { Entry: `Hello!!
    ` } ], [ "Can change tagName of fragment", { "/entry.pc": ` {children}!
    Hello! ` }, { Entry: { active: true } }, {}, { Entry: `
    Hello!!
    ` } ], [ "Can define class on fragment if tagname is changed", { "/entry.pc": ` {children} Hello! ` }, { Entry: { active: true } }, {}, { Entry: `
    Hello!
    ` } ], [ "Can render default component", { "/entry.pc": ` ok `, "/test.pc": `
    {children}!
    ` }, { Entry: { active: true } }, {}, { Entry: `
    ok!
    ` } ], [ "Can embed assets", { "/entry.pc": ` `, "/test.svg": "aa" }, { Entry: { active: true } }, { compilerOptions: { importAssetsAsModules: true } }, { Entry: `` } ], [ "Leaves src as-is if importAssetsAsModules is undefined", { "/entry.pc": ` `, "/test.svg": "aa" }, { Entry: { active: true } }, { compilerOptions: {} }, { Entry: `` } ], [ "Does not import asset if src is of instance", { "/entry.pc": ` `, "/test.svg": "aa" }, { Entry: { active: true } }, { compilerOptions: { importAssetsAsModules: true } }, { Entry: `` } ], [ "Can render default components within the same doc", { "/entry.pc": `
    Hello
    `, "/test.svg": "aa" }, { Entry: { active: true } }, { compilerOptions: { importAssetsAsModules: true } }, { Entry: `
    Hello
    ` } ] ]; ================================================ FILE: packages/paperclip-compiler-base-jsx/src/test/utils.ts ================================================ import { createEngineDelegate } from "@paperclip-ui/core"; import * as babel from "@babel/core"; import { InterimCompiler } from "@paperclip-ui/interim"; import { isPaperclipFile, PaperclipConfig } from "@paperclip-ui/utils"; export const compileModules = ( compile: (...args: any) => any, builtin: any, extensionName = "js" ) => async (graph: Record, config: PaperclipConfig) => { const engine = await createEngineDelegate({ io: { readFile: uri => graph[uri], fileExists: uri => Boolean(graph[uri]), resolveFile: (from, to) => { return to; } } }); const intermCompiler = new InterimCompiler(engine, { config, cwd: "/", targetOptions: { outDir: null }, io: { readFile(filePath) { return Buffer.from(graph[filePath]); }, getFileSize() { return 0; } } }); const modules = {}; for (const path in graph) { if (!isPaperclipFile(path)) { modules[path] = () => graph[path]; continue; } const es6 = compile({ module: intermCompiler.parseFile(path), fileUrl: path, targetOptions: config.compilerOptions || {}, config, includes: [] })["." + extensionName]; const es5 = babel.transformSync(es6, { presets: ["@babel/preset-env"] }); const module = new Function( `require`, ` const module = { exports: {} }; const exports = module.exports; ${es5.code} return exports; ` ); const executed = {}; const wrapper = () => module(path => { const mod = modules[path] || modules[path.substr(1)]; const ex = executed[path] || (executed[path] = mod && mod()); return builtin[path] || ex; }); modules[path] = wrapper; } return modules; }; ================================================ FILE: packages/paperclip-compiler-base-jsx/src/utils.ts ================================================ import { camelCase } from "lodash"; import * as path from "path"; import { Element, AS_ATTR_NAME, getAttributeStringValue } from "@paperclip-ui/core"; import { InterimModule } from "@paperclip-ui/interim"; import { CompilerOptions, PaperclipConfig, StringPosition } from "@paperclip-ui/utils"; import { SourceNode } from "source-map"; export type Context = { module: InterimModule; config: PaperclipConfig; filePath: string; buffer: any[]; depth: number; isNewLine: boolean; targetOptions: CompilerOptions; indent: string; }; const DEFAULT_TAG_NAME = "$$Default"; export const createTranslateContext = ( module: InterimModule, filePath: string, config: PaperclipConfig, targetOptions: CompilerOptions ): Context => ({ module, filePath, config, buffer: [], targetOptions, depth: 0, isNewLine: true, indent: " " }); export const RENAME_PROPS = { class: "className", autofocus: "autoFocus", autocomplete: "autoComplete", for: "htmlFor" }; export const REV_PROP = { className: "class", autoFocus: "autofocus", autoComplete: "autocomplete", htmlFor: "for" }; export const pascalCase = (value: string) => { const newValue = camelCase(value); return newValue.charAt(0).toUpperCase() + newValue.substr(1); }; export const getBaseComponentName = (filePath: string) => { return `Base${getComponentName(filePath)}`; }; export const getComponentName = (filePath: string) => { return pascalCase( `${path .basename(filePath) .split(".") .shift()}View` ); }; export const getPartClassName = (part: Element, filePath: string) => { return strToClassName(getAttributeStringValue(AS_ATTR_NAME, part), filePath); }; export const strToClassName = (value: string, filePath: string) => { let safeClassName = value.replace(/[^\w_$]/g, ""); if (safeClassName === "default") { safeClassName = getComponentName(filePath); } if (!isNaN(Number(safeClassName.charAt(0)))) { safeClassName = "_" + safeClassName; } return safeClassName; }; export const classNameToStyleName = (value: string) => { return value.charAt(0).toLowerCase() + value.substr(1); }; export const getElementInstanceName = ( tagName: string, innerTagName: string, context: Context ) => { const imp = context.module.imports.find(imp => imp.namespace === tagName); if (!imp) { if (tagName === "default") { return DEFAULT_TAG_NAME; } return tagName; } return ( "_" + camelCase(imp.publicScopeId) + (innerTagName ? "_" + innerTagName : "") ); }; export const arrayJoin = (buffer: any[], sep: string) => buffer.reduce((ary, part, index, buffer) => { ary.push(part); if (index !== buffer.length - 1) { ary.push(sep); } return ary; }, []); export const addBuffer = (buffer: any[]) => (context: Context): Context => buffer.reduce((context, part) => { if (!part) { return context; } if (typeof part === "function") { return part(context) || []; } return { ...context, buffer: [ ...context.buffer, context.isNewLine ? context.indent.repeat(context.depth) : "", part ], isNewLine: typeof part === "string" && part.lastIndexOf("\n") === part.length - 1 }; }, context); export const startBlock = (context: Context) => { return { ...context, depth: context.depth + 1 }; }; export const wrapSourceNode = ( pos: StringPosition, bufferStart: number, context: Context ) => { return { ...context, buffer: [ ...context.buffer.slice(0, bufferStart), new SourceNode(pos.line, pos.column, context.filePath, [ ...context.buffer.slice(bufferStart) ]) ] }; }; export type ContextWriter = (context: Context) => Context; export const writeSourceNode = ( pos: StringPosition | undefined, write: ContextWriter ) => (context: Context) => { const bufferStart = context.buffer.length; if (!pos) { return context; } return wrapSourceNode(pos, bufferStart, write(context)); }; export const writeJoin = ( items: TItem[], join: string, write: (item: TItem) => ContextWriter, trailing = false ) => (context: Context) => items.reduce((context, item, index, items) => { context = write(item)(context); if (index < items.length - 1 || trailing) { context = addBuffer([join])(context); } return context; }, context); export const endBlock = (context: Context) => { return { ...context, depth: context.depth - 1 }; }; ================================================ FILE: packages/paperclip-compiler-base-jsx/tsconfig.json ================================================ { "compilerOptions": { "outDir": "lib", "target": "es5", "moduleResolution": "node", "module": "commonjs", "noImplicitAny": false, "allowSyntheticDefaultImports": true, "declaration": true }, "exclude": [ "node_modules", "*-test", "lib", "index.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-compiler-html/.eslintrc.json ================================================ { "root": true, "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], "rules": { "no-constant-condition": 0, "@typescript-eslint/explicit-module-boundary-types": 0 } } ================================================ FILE: packages/paperclip-compiler-html/README.md ================================================ WIP ================================================ FILE: packages/paperclip-compiler-html/package.json ================================================ { "name": "@paperclip-ui/compiler-html", "version": "18.5.6", "description": "HTML compiler for Paperclip", "main": "lib/index.js", "scripts": { "lint": "eslint src", "test": "mocha lib/test --timeout=20000", "build": "tsc", "build:watch": "tsc --watch" }, "publishConfig": { "access": "public" }, "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.16.7", "@babel/preset-env": "^7.16.7", "@types/mocha": "^8.0.0", "@typescript-eslint/eslint-plugin": "^3.6.1", "@typescript-eslint/parser": "^3.6.1", "chai": "^4.2.0", "eslint": "^7.4.0", "mocha": "^8.0.1" }, "dependencies": { "@paperclip-ui/compiler-base-jsx": "^18.5.6", "@paperclip-ui/interim": "^18.5.6", "@paperclip-ui/utils": "^18.5.6", "lodash": "^4.17.21", "source-map": "^0.7.3" } } ================================================ FILE: packages/paperclip-compiler-html/src/index.ts ================================================ import { compilers } from "@paperclip-ui/compiler-base-jsx"; import { CompilerOptions } from "@paperclip-ui/utils"; import { TargetCompiler } from "@paperclip-ui/interim"; const CODE_PREFLIGHT = ` const encode = (value) => value.replace(/['<>\\u00A0-\u9999<>\\&]/gim, function(i) { return '&#'+i.charCodeAt(0)+';'; }); const flatten = v => v.reduce((ary, v) => { if (Array.isArray(v)) { ary.push(...flatten(v)); } else { ary.push(v); } return ary; }, []); const VOID_TAGS = ["br", "img", "hr"]; const isVoid = (v) => VOID_TAGS.includes(v); const createElement = (tag, attributes, ...children) => { if (typeof tag === "function") { return tag({ ...attributes, children }); } const buffer = ["<" + tag + ""]; for (let key in attributes) { let value = attributes[key]; if (key === "className") { key = "class"; } if (key === "ref") { continue; } if (key === "style") { value = stringifyStyle(value); } else if (typeof value === "string") { value = encode(value); } buffer.push(" " + key + "=\\"" + value + "\\""); } if (isVoid(tag)) { buffer.push("/>"); return buffer.join(""); } buffer.push(">"); buffer.push(flatten(children).filter(v => v !== false && v != null).join("")); buffer.push(""); return buffer.join(""); } const stringifyStyle = (value) => Object.entries(value).map(([key, value]) => ( key + ":" + value )).join(";"); const Fragment = (props) => flatten(props.children).join(""); const _vanilla = { createElement, Fragment, forwardRef: v => v, memo: v => v }; `.trim(); export const compile: TargetCompiler = compilers({ code: { imports: `import React from "react";\n`, preflight: CODE_PREFLIGHT, vendorName: "_vanilla" }, definition: { imports: "", elementType: "string" }, extensionName: (targetOptions: CompilerOptions) => targetOptions.es5 ? "js" : "mjs" }); ================================================ FILE: packages/paperclip-compiler-html/src/test/basic-test.ts ================================================ import * as React from "react"; import { expect } from "chai"; import { compile } from ".."; import { TEST_SUITE, compileModules } from "@paperclip-ui/compiler-base-jsx/lib/test"; describe(__filename + "#", () => { TEST_SUITE.forEach(([title, graph, contexts, config, expected]: any) => { it(title, async () => { const modules = await compileModules( compile, { react: React }, "mjs" )(graph, config); const entry = modules["/entry.pc"](); for (const componentName in contexts) { const render = entry[componentName]; expect(render(contexts[componentName])).to.eql(expected[componentName]); } }); }); }); ================================================ FILE: packages/paperclip-compiler-html/src/utils.ts ================================================ import { camelCase } from "lodash"; import * as path from "path"; import { Element, AS_ATTR_NAME, getAttributeStringValue } from "@paperclip-ui/core"; import { InterimModule } from "@paperclip-ui/interim"; import { PaperclipConfig, StringPosition } from "@paperclip-ui/utils"; import { SourceNode } from "source-map"; export type Context = { module: InterimModule; config: PaperclipConfig; filePath: string; buffer: any[]; depth: number; isNewLine: boolean; indent: string; }; const DEFAULT_TAG_NAME = "$$Default"; export const createTranslateContext = ( module: InterimModule, filePath: string, config: PaperclipConfig ): Context => ({ module, filePath, config, buffer: [], depth: 0, isNewLine: true, indent: " " }); export const RENAME_PROPS = { class: "className", autofocus: "autoFocus", autocomplete: "autoComplete", for: "htmlFor" }; export const REV_PROP = { className: "class", autoFocus: "autofocus", autoComplete: "autocomplete", htmlFor: "for" }; export const pascalCase = (value: string) => { const newValue = camelCase(value); return newValue.charAt(0).toUpperCase() + newValue.substr(1); }; export const getBaseComponentName = (filePath: string) => { return `Base${getComponentName(filePath)}`; }; export const getComponentName = (filePath: string) => { return pascalCase( `${path .basename(filePath) .split(".") .shift()}View` ); }; export const getPartClassName = (part: Element, filePath: string) => { return strToClassName(getAttributeStringValue(AS_ATTR_NAME, part), filePath); }; export const strToClassName = (value: string, filePath: string) => { let safeClassName = value.replace(/[^\w_$]/g, ""); if (safeClassName === "default") { safeClassName = getComponentName(filePath); } if (!isNaN(Number(safeClassName.charAt(0)))) { safeClassName = "_" + safeClassName; } return safeClassName; }; export const classNameToStyleName = (value: string) => { return value.charAt(0).toLowerCase() + value.substr(1); }; export const getElementInstanceName = ( tagName: string, innerTagName: string, context: Context ) => { const imp = context.module.imports.find(imp => imp.namespace === tagName); if (!imp) { if (tagName === "default") { return DEFAULT_TAG_NAME; } return tagName; } return ( "_" + camelCase(imp.publicScopeId) + (innerTagName ? "_" + innerTagName : "") ); }; export const arrayJoin = (buffer: any[], sep: string) => buffer.reduce((ary, part, index, buffer) => { ary.push(part); if (index !== buffer.length - 1) { ary.push(sep); } return ary; }, []); export const addBuffer = (buffer: any[]) => (context: Context): Context => buffer.reduce((context, part) => { if (!part) { return context; } if (typeof part === "function") { return part(context) || []; } return { ...context, buffer: [ ...context.buffer, context.isNewLine ? context.indent.repeat(context.depth) : "", part ], isNewLine: typeof part === "string" && part.lastIndexOf("\n") === part.length - 1 }; }, context); export const startBlock = (context: Context) => { return { ...context, depth: context.depth + 1 }; }; export const wrapSourceNode = ( pos: StringPosition, bufferStart: number, context: Context ) => { return { ...context, buffer: [ ...context.buffer.slice(0, bufferStart), new SourceNode(pos.line, pos.column, context.filePath, [ ...context.buffer.slice(bufferStart) ]) ] }; }; export type ContextWriter = (context: Context) => Context; export const writeSourceNode = ( pos: StringPosition | undefined, write: ContextWriter ) => (context: Context) => { const bufferStart = context.buffer.length; if (!pos) { return context; } return wrapSourceNode(pos, bufferStart, write(context)); }; export const writeJoin = ( items: TItem[], join: string, write: (item: TItem) => ContextWriter, trailing = false ) => (context: Context) => items.reduce((context, item, index, items) => { context = write(item)(context); if (index < items.length - 1 || trailing) { context = addBuffer([join])(context); } return context; }, context); export const endBlock = (context: Context) => { return { ...context, depth: context.depth - 1 }; }; ================================================ FILE: packages/paperclip-compiler-html/tsconfig.json ================================================ { "compilerOptions": { "outDir": "lib", "target": "es5", "moduleResolution": "node", "module": "commonjs", "noImplicitAny": false, "allowSyntheticDefaultImports": true }, "exclude": [ "node_modules", "*-test", "lib", "index.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-compiler-php/examples/test/src/test.pc.php ================================================ {$children} "; } function grocery_list_item($props, $children = "") { return "
  • {$children}
  • "; } ?> ================================================ FILE: packages/paperclip-compiler-react/.eslintrc.json ================================================ { "root": true, "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], "rules": { "no-constant-condition": 0, "@typescript-eslint/explicit-module-boundary-types": 0 } } ================================================ FILE: packages/paperclip-compiler-react/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/paperclip-compiler-react/README.md ================================================ React Compiler target for Paperclip Installation: `npm install @paperclip-ui/compiler-react --save-dev` Documentation: http://paperclip.dev/docs/usage-react ================================================ FILE: packages/paperclip-compiler-react/index.d.ts ================================================ export * from "./lib"; ================================================ FILE: packages/paperclip-compiler-react/index.js ================================================ module.exports = require("./lib"); ================================================ FILE: packages/paperclip-compiler-react/inernals.md ================================================ #### What does a compiled template look like? TODO ================================================ FILE: packages/paperclip-compiler-react/package.json ================================================ { "name": "@paperclip-ui/compiler-react", "version": "18.5.6", "description": "React compiler for Paperclip", "main": "index.js", "typings": "index.d.ts", "directories": { "lib": "lib" }, "dependencies": { "@paperclip-ui/compiler-base-jsx": "^18.5.6", "@paperclip-ui/core": "^18.5.6", "@paperclip-ui/interim": "^18.5.6", "crc32": "^0.2.2", "html-entities": "^1.2.1", "lodash": "^4.17.15", "source-map": "^0.7.3" }, "publishConfig": { "access": "public" }, "scripts": { "build": "tsc", "lint": "eslint src", "build:watch": "tsc --watch --preserveWatchOutput", "test": "mocha -R spec lib/test/**/*-test.js --timeout=10000" }, "author": "", "license": "MIT", "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a", "devDependencies": { "@babel/core": "^7.10.4", "@babel/preset-env": "^7.10.4", "@babel/types": "^7.10.4", "@types/mocha": "^8.0.0", "@types/node": "^14.0.20", "@types/react": "^17.0.37", "@types/react-dom": "^17.0.11", "@typescript-eslint/eslint-plugin": "^3.6.1", "@typescript-eslint/parser": "^3.6.1", "chai": "^4.2.0", "enzyme": "^3.11.0", "enzyme-adapter-react-15": "^1.4.1", "enzyme-adapter-react-16": "^1.15.2", "eslint": "^7.4.0", "mocha": "^8.0.1", "react": "^17.0.1", "react-dom": "^17.0.1", "typescript": "^4.2.3" } } ================================================ FILE: packages/paperclip-compiler-react/src/index.ts ================================================ import { compilers } from "@paperclip-ui/compiler-base-jsx"; import { TargetCompiler } from "@paperclip-ui/interim"; export const compile: TargetCompiler = compilers({ code: { preflight: "", imports: `import React from "react";\n`, vendorName: "React" }, definition: { imports: `import {ReactElement} from "react";\n`, elementType: "ReactElement" }, extensionName: "js" }); ================================================ FILE: packages/paperclip-compiler-react/src/test/basic-test.tsx ================================================ import * as React from "react"; import { expect } from "chai"; import { compile } from ".."; import * as ez from "enzyme"; import * as Adapter from "enzyme-adapter-react-16"; import { TEST_SUITE, compileModules } from "@paperclip-ui/compiler-base-jsx/lib/test"; ez.configure({ adapter: new Adapter() }); describe(__filename + "#", () => { TEST_SUITE.forEach(([title, graph, contexts, config, expected]: any) => { it(title, async () => { const modules = await compileModules(compile, { react: React })(graph, config); const entry = modules["/entry.pc"](); for (const componentName in contexts) { const Component = entry[componentName]; const renderedElement = ez.shallow( ); expect(renderedElement.html()).to.eql(expected[componentName]); } }); }); }); ================================================ FILE: packages/paperclip-compiler-react/src/test/css-test.tsx ================================================ import * as React from "react"; import { expect } from "chai"; import { compile } from ".."; import * as ez from "enzyme"; import { compileModules } from "@paperclip-ui/compiler-base-jsx/lib/test/utils"; import * as Adapter from "enzyme-adapter-react-16"; ez.configure({ adapter: new Adapter() }); describe(__filename + "#", () => { [ [ "can render a simple module", { "/entry.pc": `
    ` }, { a: "_pub-80f4925f_a" } ] ].forEach(([title, graph, expectedCSS]: any) => { it(title, async () => { const modules = await compileModules(compile, { react: React })(graph, { srcDir: null, compilerOptions: { outDir: null } }); const entry = modules["/entry.pc"](); expect(entry.classNames).to.eql(expectedCSS); }); }); }); ================================================ FILE: packages/paperclip-compiler-react/tsconfig.json ================================================ { "compilerOptions": { "outDir": "lib", "target": "es5", "declaration": true, "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true, "jsx": "react" }, "exclude": ["node_modules", "*-test", "lib", "index.d.ts", "test"], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-core/.eslintrc.json ================================================ { "root": true, "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], "rules": { "no-constant-condition": 0, "@typescript-eslint/explicit-module-boundary-types": 0, "@typescript-eslint/no-unused-vars": 2, "@typescript-eslint/no-var-requires": 0, "no-undef": 0 } } ================================================ FILE: packages/paperclip-core/.gitignore ================================================ esm native/browser native/node ================================================ FILE: packages/paperclip-core/.npmignore ================================================ native/target ================================================ FILE: packages/paperclip-core/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/paperclip-core/README.md ================================================ ================================================ FILE: packages/paperclip-core/babel.config.json ================================================ { "plugins": [ [ "bundled-import-meta", { "mappings": { "node_modules": "/assets" }, "bundleDir": "." } ] ] } ================================================ FILE: packages/paperclip-core/browser.d.ts ================================================ import { EngineDelegate, EngineOptions } from "./src/core"; export function loadEngineDelegate( options: EngineOptions, onCrash: any ): Promise; ================================================ FILE: packages/paperclip-core/browser.js ================================================ import { EngineDelegate, EngineMode } from "./esm/core"; export { EngineMode }; export const loadEngineDelegate = async (options, onCrash) => { // need this here since webpack tree shakes it out await import("./native/browser/paperclip_bg.wasm"); const { NativeEngine } = await import("./native/browser/paperclip_bg.js"); const { readFile, fileExists, resolveFile, getLintConfig } = options.io || {}; return new EngineDelegate( NativeEngine.new( readFile, fileExists, resolveFile, getLintConfig, options.mode || EngineMode.MultiFrame ), options.io, onCrash || function (e) { console.error(e); } ); }; ================================================ FILE: packages/paperclip-core/examples/codepen/bird-game.pc ================================================
    GAME OVER

    KILL THE BIRDS (shoot the birds)

    PURE CSS GAME - NO JS!

    SCORE:
    ================================================ FILE: packages/paperclip-core/examples/codepen/pure-css-accordion.pc ================================================

    Pure CSS Accordion 2.0

    Open multiple

    Lorem ipsum dolor sit amet consectetur, adipisicing elit. Ipsum, reiciendis!
    Lorem ipsum dolor sit amet consectetur adipisicing elit. A, in!

    Open one

    Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eos, facilis.
    Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nihil, aut.
    ================================================ FILE: packages/paperclip-core/examples/codepen/pure-css-buttons.pc ================================================

    Toggle 'em

    • Light

    • iOS

    • Skewed

    • Flat

    • Flip

    ================================================ FILE: packages/paperclip-core/examples/paperclip.config.json ================================================ { "srcDir": "." } ================================================ FILE: packages/paperclip-core/examples/test.css ================================================ .color { } ================================================ FILE: packages/paperclip-core/index.d.ts ================================================ import { EngineDelegate, EngineOptions } from "./src/core"; export * from "./src/core"; export * from "./src/node"; export function createEngineDelegate( options?: EngineOptions, onCrash?: any ): EngineDelegate; ================================================ FILE: packages/paperclip-core/index.js ================================================ const core = require("./lib/core"); const node = require("./lib/node"); // const { NativeEngine } = require("./native/node/paperclip"); // const fs = require("fs"); // const url = require("url"); // const path = require("path"); // const { EngineMode, EngineDelegate } = lib; // const { resolveImportUri } = require("@paperclip-ui/utils"); // const existsSyncCaseSensitive = (uri) => { // const pathname = url.fileURLToPath(uri); // const dir = path.dirname(pathname); // const basename = path.basename(pathname); // return fs.readdirSync(dir).includes(basename); // }; // const getIOOptions = (options) => { // const resolveFile = resolveImportUri(fs); // return Object.assign( // { // readFile: (uri) => { // // eslint-disable-next-line // return fs.readFileSync(new URL(uri), "utf8"); // }, // fileExists: (uri) => { // try { // // eslint-disable-next-line // const url = new URL(uri); // // need to make sure that case matches _exactly_ since some // // systems are sensitive to that. // return existsSyncCaseSensitive(url) && fs.lstatSync(url).isFile(); // } catch (e) { // // eslint-disable-next-line // console.error(e); // return false; // } // }, // resolveFile: (from, to) => { // return resolveFile(from, to); // }, // mode: EngineMode.SingleFrame, // }, // options.io, // { mode: options.mode } // ); // }; // const createEngineDelegate = (options, onCrash) => { // const { // readFile, // fileExists, // resolveFile, // mode = EngineMode.SingleFrame, // } = getIOOptions(options || {}); // return new EngineDelegate( // NativeEngine.new(readFile, fileExists, resolveFile, mode), // onCrash || // function (e) { // console.error(e); // } // ); // }; // module.exports = { // ...lib, // createEngineDelegate, // }; module.exports = { ...core, ...node }; ================================================ FILE: packages/paperclip-core/native/Cargo.toml ================================================ [profile.release] debug = false opt-level = 3 lto = true [package] name = "paperclip" version = "0.1.0" authors = ["Craig Condon "] edition = "2018" exclude = ["artifacts.json", "index.node"] [lib] name = "paperclip" crate-type = ["cdylib"] [dependencies] matches = "0.1.9" uuid = { version = "0.8", features = ["serde", "v4", "wasm-bindgen"] } serde = { version = "1.0.130", features = ["derive"] } serde_json = { version = "1.0.72" } crc = "1.8.1" web-sys = "0.3.55" console_error_panic_hook = "0.1.6" console_log = "0.2.0" cached = "0.26.2" futures = "0.3.18" regex = "1.5.4" to_absolute = "0.1.0" js-sys = "0.3.55" serde_derive = "1.0.130" wasm-bindgen-futures = "0.4.28" lazy_static = "1.4.0" [dependencies.wasm-bindgen] version = "^0.2" features = ["serde-serialize"] ================================================ FILE: packages/paperclip-core/native/artifacts.json ================================================ { "active": "release", "targets": { "release": { "rustc": "", "env": { "npm_config_target": "6.1.7", "npm_config_arch": "x64", "npm_config_target_arch": "x64", "npm_config_disturl": "https://atom.io/download/electron", "npm_config_runtime": "electron", "npm_config_build_from_source": "true", "npm_config_devdir": "/Users/crcn/.electron-gyp" } } } } ================================================ FILE: packages/paperclip-core/native/build.rsx ================================================ use neon_build; fn main() { neon_build::setup(); // must be called in build.rs } ================================================ FILE: packages/paperclip-core/native/bundler/paperclip.d.ts ================================================ /* tslint:disable */ /* eslint-disable */ /** */ export enum NativeEngineMode { SingleFrame, MultiFrame } /** */ export class NativeEngine { free(): void; /** * @param {Function} read_file * @param {Function} file_exists * @param {Function} resolve_file * @param {Function} get_lint_config * @param {number} engine_mode * @returns {NativeEngine} */ static new( read_file: Function, file_exists: Function, resolve_file: Function, get_lint_config: Function, engine_mode: number ): NativeEngine; /** * @param {string} uri * @returns {any} */ load(uri: string): any; /** * @param {string} uri * @returns {any} */ run(uri: string): any; /** * @returns {any} */ get_graph_uris(): any; /** */ reset(): void; /** * @param {Function} listener */ add_listener(listener: Function): void; /** * @param {string} uri * @returns {any} */ lint_file(uri: string): any; /** * @param {Uint32Array} path * @param {string} uri * @returns {any} */ get_virtual_node_source_info(path: Uint32Array, uri: string): any; /** * @param {string} uri * @returns {any} */ get_loaded_ast(uri: string): any; /** * @param {string} uri * @returns {any} */ get_dependency(uri: string): any; /** * @param {string} content * @param {string} uri * @returns {any} */ parse_content(content: string, uri: string): any; /** * @param {string} uri * @returns {any} */ parse_file(uri: string): any; /** */ purge_unlinked_files(): void; /** * @param {string} id * @returns {any} */ get_expression_by_id(id: string): any; /** * @param {Uint32Array} path * @param {string} uri * @param {number} screen_width * @returns {any} */ inspect_node_styles( path: Uint32Array, uri: string, screen_width: number ): any; /** * @param {string} uri * @param {string} content */ update_virtual_file_content(uri: string, content: string): void; } ================================================ FILE: packages/paperclip-core/native/bundler/paperclip.js ================================================ import * as wasm from "./paperclip_bg.wasm"; export * from "./paperclip_bg.js"; ================================================ FILE: packages/paperclip-core/native/bundler/paperclip_bg.js ================================================ import * as wasm from "./paperclip_bg.wasm"; const heap = new Array(32).fill(undefined); heap.push(undefined, null, true, false); function getObject(idx) { return heap[idx]; } let heap_next = heap.length; function dropObject(idx) { if (idx < 36) return; heap[idx] = heap_next; heap_next = idx; } function takeObject(idx) { const ret = getObject(idx); dropObject(idx); return ret; } function addHeapObject(obj) { if (heap_next === heap.length) heap.push(heap.length + 1); const idx = heap_next; heap_next = heap[idx]; heap[idx] = obj; return idx; } const lTextDecoder = typeof TextDecoder === "undefined" ? (0, module.require)("util").TextDecoder : TextDecoder; let cachedTextDecoder = new lTextDecoder("utf-8", { ignoreBOM: true, fatal: true }); cachedTextDecoder.decode(); let cachegetUint8Memory0 = null; function getUint8Memory0() { if ( cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer ) { cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); } return cachegetUint8Memory0; } function getStringFromWasm0(ptr, len) { return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); } let WASM_VECTOR_LEN = 0; const lTextEncoder = typeof TextEncoder === "undefined" ? (0, module.require)("util").TextEncoder : TextEncoder; let cachedTextEncoder = new lTextEncoder("utf-8"); const encodeString = typeof cachedTextEncoder.encodeInto === "function" ? function(arg, view) { return cachedTextEncoder.encodeInto(arg, view); } : function(arg, view) { const buf = cachedTextEncoder.encode(arg); view.set(buf); return { read: arg.length, written: buf.length }; }; function passStringToWasm0(arg, malloc, realloc) { if (realloc === undefined) { const buf = cachedTextEncoder.encode(arg); const ptr = malloc(buf.length); getUint8Memory0() .subarray(ptr, ptr + buf.length) .set(buf); WASM_VECTOR_LEN = buf.length; return ptr; } let len = arg.length; let ptr = malloc(len); const mem = getUint8Memory0(); let offset = 0; for (; offset < len; offset++) { const code = arg.charCodeAt(offset); if (code > 0x7f) break; mem[ptr + offset] = code; } if (offset !== len) { if (offset !== 0) { arg = arg.slice(offset); } ptr = realloc(ptr, len, (len = offset + arg.length * 3)); const view = getUint8Memory0().subarray(ptr + offset, ptr + len); const ret = encodeString(arg, view); offset += ret.written; } WASM_VECTOR_LEN = offset; return ptr; } let cachegetInt32Memory0 = null; function getInt32Memory0() { if ( cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer ) { cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); } return cachegetInt32Memory0; } function isLikeNone(x) { return x === undefined || x === null; } function debugString(val) { // primitive types const type = typeof val; if (type == "number" || type == "boolean" || val == null) { return `${val}`; } if (type == "string") { return `"${val}"`; } if (type == "symbol") { const description = val.description; if (description == null) { return "Symbol"; } else { return `Symbol(${description})`; } } if (type == "function") { const name = val.name; if (typeof name == "string" && name.length > 0) { return `Function(${name})`; } else { return "Function"; } } // objects if (Array.isArray(val)) { const length = val.length; let debug = "["; if (length > 0) { debug += debugString(val[0]); } for (let i = 1; i < length; i++) { debug += ", " + debugString(val[i]); } debug += "]"; return debug; } // Test for built-in const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); let className; if (builtInMatches.length > 1) { className = builtInMatches[1]; } else { // Failed to match the standard '[object ClassName]' return toString.call(val); } if (className == "Object") { // we're a user defined class or Object // JSON.stringify avoids problems with cycles, and is generally much // easier than looping through ownProperties of `val`. try { return "Object(" + JSON.stringify(val) + ")"; } catch (_) { return "Object"; } } // errors if (val instanceof Error) { return `${val.name}: ${val.message}\n${val.stack}`; } // TODO we could test for more things here, like `Set`s and `Map`s. return className; } function handleError(f, args) { try { return f.apply(this, args); } catch (e) { wasm.__wbindgen_exn_store(addHeapObject(e)); } } function getArrayU8FromWasm0(ptr, len) { return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); } let cachegetUint32Memory0 = null; function getUint32Memory0() { if ( cachegetUint32Memory0 === null || cachegetUint32Memory0.buffer !== wasm.memory.buffer ) { cachegetUint32Memory0 = new Uint32Array(wasm.memory.buffer); } return cachegetUint32Memory0; } function passArray32ToWasm0(arg, malloc) { const ptr = malloc(arg.length * 4); getUint32Memory0().set(arg, ptr / 4); WASM_VECTOR_LEN = arg.length; return ptr; } /** */ export const NativeEngineMode = Object.freeze({ SingleFrame: 0, "0": "SingleFrame", MultiFrame: 1, "1": "MultiFrame" }); /** */ export class NativeEngine { static __wrap(ptr) { const obj = Object.create(NativeEngine.prototype); obj.ptr = ptr; return obj; } __destroy_into_raw() { const ptr = this.ptr; this.ptr = 0; return ptr; } free() { const ptr = this.__destroy_into_raw(); wasm.__wbg_nativeengine_free(ptr); } /** * @param {Function} read_file * @param {Function} file_exists * @param {Function} resolve_file * @param {Function} get_lint_config * @param {number} engine_mode * @returns {NativeEngine} */ static new( read_file, file_exists, resolve_file, get_lint_config, engine_mode ) { var ret = wasm.nativeengine_new( addHeapObject(read_file), addHeapObject(file_exists), addHeapObject(resolve_file), addHeapObject(get_lint_config), engine_mode ); return NativeEngine.__wrap(ret); } /** * @param {string} uri * @returns {any} */ load(uri) { var ptr0 = passStringToWasm0( uri, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len0 = WASM_VECTOR_LEN; var ret = wasm.nativeengine_load(this.ptr, ptr0, len0); return takeObject(ret); } /** * @param {string} uri * @returns {any} */ run(uri) { var ptr0 = passStringToWasm0( uri, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len0 = WASM_VECTOR_LEN; var ret = wasm.nativeengine_run(this.ptr, ptr0, len0); return takeObject(ret); } /** * @returns {any} */ get_graph_uris() { var ret = wasm.nativeengine_get_graph_uris(this.ptr); return takeObject(ret); } /** */ reset() { wasm.nativeengine_reset(this.ptr); } /** * @param {Function} listener */ add_listener(listener) { wasm.nativeengine_add_listener(this.ptr, addHeapObject(listener)); } /** * @param {string} uri * @returns {any} */ lint_file(uri) { var ptr0 = passStringToWasm0( uri, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len0 = WASM_VECTOR_LEN; var ret = wasm.nativeengine_lint_file(this.ptr, ptr0, len0); return takeObject(ret); } /** * @param {Uint32Array} path * @param {string} uri * @returns {any} */ get_virtual_node_source_info(path, uri) { var ptr0 = passArray32ToWasm0(path, wasm.__wbindgen_malloc); var len0 = WASM_VECTOR_LEN; var ptr1 = passStringToWasm0( uri, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len1 = WASM_VECTOR_LEN; var ret = wasm.nativeengine_get_virtual_node_source_info( this.ptr, ptr0, len0, ptr1, len1 ); return takeObject(ret); } /** * @param {string} uri * @returns {any} */ get_loaded_ast(uri) { var ptr0 = passStringToWasm0( uri, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len0 = WASM_VECTOR_LEN; var ret = wasm.nativeengine_get_loaded_ast(this.ptr, ptr0, len0); return takeObject(ret); } /** * @param {string} uri * @returns {any} */ get_dependency(uri) { var ptr0 = passStringToWasm0( uri, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len0 = WASM_VECTOR_LEN; var ret = wasm.nativeengine_get_dependency(this.ptr, ptr0, len0); return takeObject(ret); } /** * @param {string} content * @param {string} uri * @returns {any} */ parse_content(content, uri) { var ptr0 = passStringToWasm0( content, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len0 = WASM_VECTOR_LEN; var ptr1 = passStringToWasm0( uri, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len1 = WASM_VECTOR_LEN; var ret = wasm.nativeengine_parse_content(this.ptr, ptr0, len0, ptr1, len1); return takeObject(ret); } /** * @param {string} uri * @returns {any} */ parse_file(uri) { var ptr0 = passStringToWasm0( uri, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len0 = WASM_VECTOR_LEN; var ret = wasm.nativeengine_parse_file(this.ptr, ptr0, len0); return takeObject(ret); } /** */ purge_unlinked_files() { wasm.nativeengine_purge_unlinked_files(this.ptr); } /** * @param {string} id * @returns {any} */ get_expression_by_id(id) { var ptr0 = passStringToWasm0( id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len0 = WASM_VECTOR_LEN; var ret = wasm.nativeengine_get_expression_by_id(this.ptr, ptr0, len0); return takeObject(ret); } /** * @param {Uint32Array} path * @param {string} uri * @param {number} screen_width * @returns {any} */ inspect_node_styles(path, uri, screen_width) { var ptr0 = passArray32ToWasm0(path, wasm.__wbindgen_malloc); var len0 = WASM_VECTOR_LEN; var ptr1 = passStringToWasm0( uri, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len1 = WASM_VECTOR_LEN; var ret = wasm.nativeengine_inspect_node_styles( this.ptr, ptr0, len0, ptr1, len1, screen_width ); return takeObject(ret); } /** * @param {string} uri * @param {string} content */ update_virtual_file_content(uri, content) { var ptr0 = passStringToWasm0( uri, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len0 = WASM_VECTOR_LEN; var ptr1 = passStringToWasm0( content, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len1 = WASM_VECTOR_LEN; wasm.nativeengine_update_virtual_file_content( this.ptr, ptr0, len0, ptr1, len1 ); } } export function __wbg_new_693216e109162396() { var ret = new Error(); return addHeapObject(ret); } export function __wbg_stack_0ddaca5d1abfb52f(arg0, arg1) { var ret = getObject(arg1).stack; var ptr0 = passStringToWasm0( ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len0 = WASM_VECTOR_LEN; getInt32Memory0()[arg0 / 4 + 1] = len0; getInt32Memory0()[arg0 / 4 + 0] = ptr0; } export function __wbg_error_09919627ac0992f5(arg0, arg1) { try { console.error(getStringFromWasm0(arg0, arg1)); } finally { wasm.__wbindgen_free(arg0, arg1); } } export function __wbindgen_object_drop_ref(arg0) { takeObject(arg0); } export function __wbindgen_object_clone_ref(arg0) { var ret = getObject(arg0); return addHeapObject(ret); } export function __wbg_process_2f24d6544ea7b200(arg0) { var ret = getObject(arg0).process; return addHeapObject(ret); } export function __wbindgen_is_object(arg0) { const val = getObject(arg0); var ret = typeof val === "object" && val !== null; return ret; } export function __wbg_versions_6164651e75405d4a(arg0) { var ret = getObject(arg0).versions; return addHeapObject(ret); } export function __wbg_node_4b517d861cbcb3bc(arg0) { var ret = getObject(arg0).node; return addHeapObject(ret); } export function __wbindgen_is_string(arg0) { var ret = typeof getObject(arg0) === "string"; return ret; } export function __wbg_modulerequire_3440a4bcf44437db() { return handleError(function(arg0, arg1) { var ret = module.require(getStringFromWasm0(arg0, arg1)); return addHeapObject(ret); }, arguments); } export function __wbg_crypto_98fc271021c7d2ad(arg0) { var ret = getObject(arg0).crypto; return addHeapObject(ret); } export function __wbg_msCrypto_a2cdb043d2bfe57f(arg0) { var ret = getObject(arg0).msCrypto; return addHeapObject(ret); } export function __wbg_newwithlength_929232475839a482(arg0) { var ret = new Uint8Array(arg0 >>> 0); return addHeapObject(ret); } export function __wbg_self_c6fbdfc2918d5e58() { return handleError(function() { var ret = self.self; return addHeapObject(ret); }, arguments); } export function __wbg_window_baec038b5ab35c54() { return handleError(function() { var ret = window.window; return addHeapObject(ret); }, arguments); } export function __wbg_globalThis_3f735a5746d41fbd() { return handleError(function() { var ret = globalThis.globalThis; return addHeapObject(ret); }, arguments); } export function __wbg_global_1bc0b39582740e95() { return handleError(function() { var ret = global.global; return addHeapObject(ret); }, arguments); } export function __wbindgen_is_undefined(arg0) { var ret = getObject(arg0) === undefined; return ret; } export function __wbg_newnoargs_be86524d73f67598(arg0, arg1) { var ret = new Function(getStringFromWasm0(arg0, arg1)); return addHeapObject(ret); } export function __wbg_call_888d259a5fefc347() { return handleError(function(arg0, arg1) { var ret = getObject(arg0).call(getObject(arg1)); return addHeapObject(ret); }, arguments); } export function __wbindgen_string_new(arg0, arg1) { var ret = getStringFromWasm0(arg0, arg1); return addHeapObject(ret); } export function __wbg_call_346669c262382ad7() { return handleError(function(arg0, arg1, arg2) { var ret = getObject(arg0).call(getObject(arg1), getObject(arg2)); return addHeapObject(ret); }, arguments); } export function __wbindgen_json_serialize(arg0, arg1) { const obj = getObject(arg1); var ret = JSON.stringify(obj === undefined ? null : obj); var ptr0 = passStringToWasm0( ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len0 = WASM_VECTOR_LEN; getInt32Memory0()[arg0 / 4 + 1] = len0; getInt32Memory0()[arg0 / 4 + 0] = ptr0; } export function __wbg_call_8a893cac80deeb51() { return handleError(function(arg0, arg1, arg2, arg3) { var ret = getObject(arg0).call( getObject(arg1), getObject(arg2), getObject(arg3) ); return addHeapObject(ret); }, arguments); } export function __wbindgen_string_get(arg0, arg1) { const obj = getObject(arg1); var ret = typeof obj === "string" ? obj : undefined; var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); var len0 = WASM_VECTOR_LEN; getInt32Memory0()[arg0 / 4 + 1] = len0; getInt32Memory0()[arg0 / 4 + 0] = ptr0; } export function __wbindgen_boolean_get(arg0) { const v = getObject(arg0); var ret = typeof v === "boolean" ? (v ? 1 : 0) : 2; return ret; } export function __wbindgen_json_parse(arg0, arg1) { var ret = JSON.parse(getStringFromWasm0(arg0, arg1)); return addHeapObject(ret); } export function __wbg_randomFillSync_64cc7d048f228ca8() { return handleError(function(arg0, arg1, arg2) { getObject(arg0).randomFillSync(getArrayU8FromWasm0(arg1, arg2)); }, arguments); } export function __wbg_subarray_8b658422a224f479(arg0, arg1, arg2) { var ret = getObject(arg0).subarray(arg1 >>> 0, arg2 >>> 0); return addHeapObject(ret); } export function __wbg_getRandomValues_98117e9a7e993920() { return handleError(function(arg0, arg1) { getObject(arg0).getRandomValues(getObject(arg1)); }, arguments); } export function __wbg_length_1eb8fc608a0d4cdb(arg0) { var ret = getObject(arg0).length; return ret; } export function __wbindgen_memory() { var ret = wasm.memory; return addHeapObject(ret); } export function __wbg_buffer_397eaa4d72ee94dd(arg0) { var ret = getObject(arg0).buffer; return addHeapObject(ret); } export function __wbg_new_a7ce447f15ff496f(arg0) { var ret = new Uint8Array(getObject(arg0)); return addHeapObject(ret); } export function __wbg_set_969ad0a60e51d320(arg0, arg1, arg2) { getObject(arg0).set(getObject(arg1), arg2 >>> 0); } export function __wbindgen_debug_string(arg0, arg1) { var ret = debugString(getObject(arg1)); var ptr0 = passStringToWasm0( ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc ); var len0 = WASM_VECTOR_LEN; getInt32Memory0()[arg0 / 4 + 1] = len0; getInt32Memory0()[arg0 / 4 + 0] = ptr0; } export function __wbindgen_throw(arg0, arg1) { throw new Error(getStringFromWasm0(arg0, arg1)); } ================================================ FILE: packages/paperclip-core/native/bundler/paperclip_bg.wasm.d.ts ================================================ /* tslint:disable */ /* eslint-disable */ export const memory: WebAssembly.Memory; export function __wbg_nativeengine_free(a: number): void; export function nativeengine_new( a: number, b: number, c: number, d: number, e: number ): number; export function nativeengine_load(a: number, b: number, c: number): number; export function nativeengine_run(a: number, b: number, c: number): number; export function nativeengine_get_graph_uris(a: number): number; export function nativeengine_reset(a: number): void; export function nativeengine_add_listener(a: number, b: number): void; export function nativeengine_lint_file(a: number, b: number, c: number): number; export function nativeengine_get_virtual_node_source_info( a: number, b: number, c: number, d: number, e: number ): number; export function nativeengine_get_loaded_ast( a: number, b: number, c: number ): number; export function nativeengine_get_dependency( a: number, b: number, c: number ): number; export function nativeengine_parse_content( a: number, b: number, c: number, d: number, e: number ): number; export function nativeengine_parse_file( a: number, b: number, c: number ): number; export function nativeengine_purge_unlinked_files(a: number): void; export function nativeengine_get_expression_by_id( a: number, b: number, c: number ): number; export function nativeengine_inspect_node_styles( a: number, b: number, c: number, d: number, e: number, f: number ): number; export function nativeengine_update_virtual_file_content( a: number, b: number, c: number, d: number, e: number ): void; export function __wbindgen_malloc(a: number): number; export function __wbindgen_realloc(a: number, b: number, c: number): number; export function __wbindgen_free(a: number, b: number): void; export function __wbindgen_exn_store(a: number): void; ================================================ FILE: packages/paperclip-core/native/rustfmt.toml ================================================ tab_spaces = 2 ================================================ FILE: packages/paperclip-core/native/src/annotation/ast.rs ================================================ use crate::base::ast as base_ast; use crate::script::ast as script_ast; use serde::Serialize; #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Annotation { pub properties: Vec, pub range: base_ast::Range, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "kind")] pub enum AnnotationProperty { Declaration(Declaration), Text(Text), } // @property jsValue // @property { key: "value" } // @pane: { key: } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Declaration { pub name: String, pub value: script_ast::Expression, pub raws: base_ast::BasicRaws, pub range: base_ast::Range, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Text { pub raws: base_ast::BasicRaws, pub value: String, pub range: base_ast::Range, } ================================================ FILE: packages/paperclip-core/native/src/annotation/mod.rs ================================================ pub mod ast; pub mod parser; pub mod tokenizer; ================================================ FILE: packages/paperclip-core/native/src/annotation/parser.rs ================================================ use super::ast; use super::tokenizer::{Token, Tokenizer}; use crate::base::ast as base_ast; use crate::base::parser::{get_buffer, ParseError}; use crate::script::parser::parse_with_tokenizer as parse_js_with_tokenizer; use crate::script::tokenizer::Tokenizer as JSTokenizer; type FUntil<'a> = for<'r> fn(&mut Tokenizer<'a>) -> Result; pub struct Context<'a, 'b> { tokenizer: &'a mut Tokenizer<'b>, scope_id: String, until: FUntil<'b>, } impl<'a, 'b> Context<'a, 'b> { pub fn ended(&mut self) -> Result { Ok(self.tokenizer.is_eof() || (self.until)(self.tokenizer)?) } } pub fn parse_with_tokenizer<'a, 'b>( tokenizer: &'a mut Tokenizer<'b>, scope_id: &String, until: FUntil<'b>, ) -> Result { let mut context = Context { tokenizer, until, scope_id: scope_id.to_string(), }; parse_annotation(&mut context) } fn parse_annotation<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let start = context.tokenizer.scanner.get_u16pos(); let mut properties: Vec = vec![]; let mut raw_before = context.tokenizer.scanner.eat_whitespace(); while !context.ended()? { properties.push(parse_annotation_property(context, raw_before)?); raw_before = None; } Ok(ast::Annotation { properties, range: base_ast::Range::new(start, context.tokenizer.scanner.get_u16pos()), }) } fn parse_annotation_property<'a, 'b>( context: &mut Context<'a, 'b>, raw_before: Option<&'b [u8]>, ) -> Result { match context.tokenizer.peek(1)? { Token::At => parse_declaration_property(context, raw_before), _ => parse_text_annotation(context, raw_before), } } fn parse_text_annotation<'a, 'b>( context: &mut Context<'a, 'b>, raw_before: Option<&'a [u8]>, ) -> Result { let start = context.tokenizer.scanner.get_u16pos(); let start_u8 = context.tokenizer.get_pos().u8_pos; // Take everything except @ sign while !context.ended()? && context.tokenizer.peek(1)? != Token::At { context.tokenizer.next()?; } let end = context.tokenizer.scanner.get_u16pos(); let end_u8 = context.tokenizer.get_pos().u8_pos; let buffer = std::str::from_utf8(&context.tokenizer.scanner.source[start_u8..end_u8]).unwrap(); Ok(ast::AnnotationProperty::Text(ast::Text { raws: base_ast::BasicRaws::new(raw_before, None), value: buffer.to_string(), range: start.range_from(end), })) } fn parse_declaration_property<'a, 'b>( context: &mut Context<'a, 'b>, raw_before: Option<&'b [u8]>, ) -> Result { let start = context.tokenizer.scanner.get_u16pos(); context.tokenizer.next_expect(Token::At)?; let name = get_buffer(context.tokenizer, |tokenizer| { Ok(tokenizer.peek(1)? != Token::Byte(b' ')) })? .to_string(); let mut js_tokenizer = JSTokenizer::new_from_scanner(context.tokenizer.scanner.clone()); let value = parse_js_with_tokenizer(&mut js_tokenizer, "".to_string(), context.scope_id.as_str())?; context.tokenizer.set_pos(&js_tokenizer.scanner.get_pos()); Ok(ast::AnnotationProperty::Declaration(ast::Declaration { name, raws: base_ast::BasicRaws::new(raw_before, None), value, range: base_ast::Range::new(start, context.tokenizer.scanner.get_u16pos()), })) } ================================================ FILE: packages/paperclip-core/native/src/annotation/runtime.rs ================================================ ================================================ FILE: packages/paperclip-core/native/src/annotation/tokenizer.rs ================================================ use crate::base::parser::ParseError; use crate::base::string_scanner::{Char, Position as StringScannerPosition, StringScanner}; use crate::base::tokenizer::BaseTokenizer; #[derive(PartialEq, Debug)] pub enum Token<'a> { // @ At, Word(&'a str), Byte(u8), Escape(u8), Cluster(&'a [u8]), } pub struct Tokenizer<'a> { pub scanner: StringScanner<'a>, } impl<'a> Tokenizer<'a> { pub fn get_pos(&self) -> StringScannerPosition { self.scanner.get_pos() } pub fn peek(&mut self, steps: u8) -> Result, ParseError> { let pos = self.get_pos(); let mut i = 0; let mut result = Err(ParseError::unknown()); while i < steps { result = self.next(); i += 1; } self.set_pos(&pos); result } pub fn next_expect(&mut self, expected_token: Token) -> Result, ParseError> { let utf16_pos = self.scanner.get_u16pos(); let token = self.next()?; if token == expected_token { return Ok(token); } else { return Err(ParseError::unexpected_token( utf16_pos.range_from(self.scanner.get_u16pos()), )); } } pub fn next(&mut self) -> Result, ParseError> { if self.is_eof() { return Err(ParseError::eof()); } let c = self .scanner .curr_byte() .or_else(|_| Err(ParseError::eof()))?; match c { b'\\' => { self.scanner.forward(1); // eat slash let c = self .scanner .curr_byte() .or_else(|_| Err(ParseError::eof()))?; self.scanner.forward(1); // eat escaped Ok(Token::Escape(c)) } b'@' => { self.scanner.forward(1); Ok(Token::At) } b'a'..=b'z' | b'A'..=b'Z' => { Ok(Token::Word(self.scanner.search(|c| -> bool { matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9') }))) } _ => { let c = self .scanner .next_char() .or_else(|_| Err(ParseError::eof()))?; Ok(match c { Char::Byte(b) => Token::Byte(b), Char::Cluster(chars) => Token::Cluster(chars), }) } } } pub fn set_pos(&mut self, pos: &StringScannerPosition) { self.scanner.set_pos(pos); } pub fn is_eof(&mut self) -> bool { self.scanner.is_eof() } pub fn new_from_scanner(scanner: StringScanner<'a>) -> Tokenizer<'a> { Tokenizer { scanner: scanner } } } impl<'a> BaseTokenizer<'a> for Tokenizer<'a> { fn is_eof(&self) -> bool { self.scanner.is_eof() } fn skip(&mut self) -> Result<(), ParseError> { self.next()?; Ok(()) } fn get_range(&self) -> &'a [u8] { self.scanner.source } fn get_pos(&self) -> usize { self.scanner.pos } } ================================================ FILE: packages/paperclip-core/native/src/base/ast.rs ================================================ use super::string_scanner::U16Position; use serde::Serialize; #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Range { pub start: U16Position, pub end: U16Position, } impl Range { pub fn new(start: U16Position, end: U16Position) -> Range { Range { start, end } } pub fn nil() -> Range { Range::new(U16Position::new(0, 0, 0), U16Position::new(0, 0, 0)) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct BasicRaws { pub before: String, pub after: String, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ExprTextSource { pub uri: String, pub range: Range, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ExprSource { // source where the error is. Might not exist #[serde(rename = "sourceId")] pub id: String, // This may not exist if we're dealing with just the AST #[serde(rename = "textSource")] pub text_source: Option, } impl ExprSource { pub fn new<'a>(id: &'a str, text_source: Option<&ExprTextSource>) -> ExprSource { ExprSource { id: id.to_string(), text_source: text_source.and_then(|source| Some(source.clone())), } } } impl ExprTextSource { pub fn new(uri: String, range: Range) -> ExprTextSource { ExprTextSource { uri, range } } } // impl Eq for ExprTextSource { // fn eq(&self, other: &Self) -> bool { // self.uri == other.uri && self.location == self.location // } // } // TODO - change to trait #[derive(Debug, PartialEq, Serialize)] pub struct Expression { // TODO - location: Location pub item: TItem, } ================================================ FILE: packages/paperclip-core/native/src/base/mod.rs ================================================ pub mod ast; pub mod parser; pub mod runtime; pub mod string_scanner; pub mod tokenizer; pub mod utils; ================================================ FILE: packages/paperclip-core/native/src/base/parser.rs ================================================ use super::tokenizer::*; use crate::base::ast::Range; use serde::Serialize; #[derive(Debug, PartialEq, Serialize, Clone)] pub enum ParseErrorKind { EndOfFile, Unknown, Unexpected, Unterminated, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ParseError { pub kind: ParseErrorKind, pub message: String, pub range: Range, } impl ParseError { pub fn new(kind: ParseErrorKind, message: String, range: Range) -> ParseError { ParseError { kind, message, range, } } pub fn unexpected_token(range: Range) -> ParseError { ParseError::new( ParseErrorKind::Unexpected, "Unexpected token".to_string(), range, ) } pub fn unexpected(message: String, range: Range) -> ParseError { ParseError::new(ParseErrorKind::Unexpected, message, range) } pub fn unterminated(message: String, range: Range) -> ParseError { ParseError::new(ParseErrorKind::Unterminated, message, range) } pub fn eof() -> ParseError { ParseError::new( ParseErrorKind::EndOfFile, "End of file".to_string(), Range::nil(), ) } pub fn unknown() -> ParseError { ParseError::new( ParseErrorKind::Unknown, "An unknown error has occurred".to_string(), Range::nil(), ) } } pub fn get_buffer<'a, TTokenizer, FF>( tokenizer: &mut TTokenizer, until: FF, ) -> Result<&'a str, ParseError> where TTokenizer: BaseTokenizer<'a>, FF: Fn(&mut TTokenizer) -> Result, { let start = tokenizer.get_pos(); let mut end = start; while !tokenizer.is_eof() { if !until(tokenizer)? { break; } tokenizer.skip()?; end = tokenizer.get_pos(); } Ok(std::str::from_utf8(&tokenizer.get_range()[start..end]).unwrap()) } ================================================ FILE: packages/paperclip-core/native/src/base/runtime.rs ================================================ use super::ast; use serde::Serialize; #[derive(Debug, Clone, Serialize, PartialEq)] pub struct RuntimeError { pub uri: String, pub range: ast::Range, pub message: String, } impl RuntimeError { pub fn new(message: String, uri: &String, range: &ast::Range) -> RuntimeError { RuntimeError { message: message, uri: uri.to_string(), range: range.clone(), } } pub fn unknown(uri: &String) -> RuntimeError { RuntimeError::new( "An unknown error has occurred.".to_string(), uri, &ast::Range::nil(), ) } } ================================================ FILE: packages/paperclip-core/native/src/base/string_scanner.rs ================================================ use super::ast::Range; use serde::Serialize; #[derive(Debug, PartialEq, Serialize, Clone)] pub struct U16Position { pub pos: usize, pub line: usize, pub column: usize, } impl U16Position { pub fn new(pos: usize, line: usize, column: usize) -> U16Position { U16Position { pos: pos, line: line, column: column, } } pub fn range_from(&self, pos: U16Position) -> Range { Range::new(self.clone(), pos) } pub fn range_to(&self, pos: U16Position) -> Range { Range::new(pos, self.clone()) } } #[derive(Debug)] pub struct Position { pub u8_pos: usize, pub u16_pos: usize, pub u16_line: usize, pub u16_column: usize, } impl Position { pub fn to_u16(&self) -> U16Position { U16Position { pos: self.u16_pos, line: self.u16_line, column: self.u16_column, } } } pub enum Char<'a> { Byte(u8), Cluster(&'a [u8]), } #[derive(Clone)] pub struct StringScanner<'a> { pub source: &'a [u8], pub pos: usize, pub u16_pos: usize, pub u16_line: usize, pub u16_column: usize, len: usize, } #[derive(Debug)] pub enum StringScannerError { EOF, } impl<'a> StringScanner<'a> { pub fn eat_whitespace(&mut self) -> Option<&'a [u8]> { if self.is_eof() { return None; } let is_whitepace = |c| -> bool { matches!(c, b' ' | b'\t' | b'\r' | b'\n') }; let start = self.pos; while !self.is_eof() && is_whitepace(self.curr_byte().unwrap()) { self.forward(1); } Some(&self.source[start..self.pos]) } pub fn starts_with(&mut self, pattern: &[u8]) -> bool { self.source[self.pos..].starts_with(pattern) } pub fn get_pos(&self) -> Position { Position { u8_pos: self.pos, u16_pos: self.u16_pos, u16_line: self.u16_line, u16_column: self.u16_column, } } pub fn get_u16pos(&self) -> U16Position { U16Position { pos: self.u16_pos, line: self.u16_line, column: self.u16_column, } } pub fn set_pos(&mut self, pos: &Position) { self.pos = pos.u8_pos; self.u16_pos = pos.u16_pos; self.u16_line = pos.u16_line; self.u16_column = pos.u16_column; } pub fn new(source: &'a str) -> StringScanner<'a> { StringScanner { source: source.as_bytes(), len: source.len(), pos: 0, u16_pos: 0, u16_line: 1, u16_column: 1, } } pub fn forward(&mut self, steps: usize) { let mut subcol = 0; let new_pos = self.pos + steps; if new_pos < self.source.len() { for i in self.pos..new_pos { if i == self.len { break; } let c = self.source[i]; if c == b'\n' || c == b'\r' { subcol = i - self.pos; // reset to zero since it'll be incremented in the proceeding code. This will always be 1 self.u16_column = 0; self.u16_line += 1; } } } self.pos = new_pos; self.u16_pos += steps; self.u16_column += steps - subcol; } pub fn next_char(&mut self) -> Result, StringScannerError> { let c = self.curr_byte()?; let mut len = 1; let mut utf8_step = 1; if c < 0x80 { len = 1; } else if c < 0xC0 { len = 1; } else if c < 0xE0 { len = 2; } else if c < 0xF0 { len = 3; } else if c < 0xF8 { len = 4; utf8_step = 2; } if len == 1 { self.forward(1); Ok(Char::Byte(c)) } else { let utf8_pos = self.u16_pos; let utf8_column = self.u16_column; let buffer = &self.source[self.pos..(self.pos + len)]; self.forward(len); self.u16_pos = utf8_pos + utf8_step; self.u16_column = utf8_column + utf8_step; Ok(Char::Cluster(buffer)) } } pub fn curr_byte(&self) -> Result { if self.pos < self.source.len() { Ok(self.source[self.pos]) } else { Err(StringScannerError::EOF) } } pub fn is_eof(&self) -> bool { self.pos >= self.source.len() } pub fn search(&mut self, test: FF) -> &'a str where FF: Fn(u8) -> bool, { let start = self.pos; self.scan(test); self.since(start) } pub fn since(&mut self, start: usize) -> &'a str { std::str::from_utf8(&self.source[start..self.pos]).unwrap() } pub fn scan(&mut self, test: FF) -> Result<(), StringScannerError> where FF: Fn(u8) -> bool, { while !self.is_eof() { let pos = self.get_pos(); let c = self.next_char()?; match c { Char::Byte(b) => { if !test(b) { self.set_pos(&pos); break; } } _ => {} } } Ok(()) } } ================================================ FILE: packages/paperclip-core/native/src/base/tokenizer.rs ================================================ use super::parser::ParseError; pub trait BaseToken {} pub trait BaseTokenizer<'a> { // Static method signature; `Self` refers to the implementor type. fn is_eof(&self) -> bool; fn skip(&mut self) -> Result<(), ParseError>; fn get_pos(&self) -> usize; fn get_range(&self) -> &'a [u8]; } #[derive(Debug)] pub struct Position { pub u8_pos: usize, pub u16_pos: usize, } #[derive(PartialEq, Debug)] pub enum Token<'a> { Whitespace, Cluster(&'a [u8]), } pub struct Tokenizer<'a> { pub source: &'a [u8], pub pos: usize, pub utf16_pos: usize, } impl<'a> Tokenizer<'a> { pub fn next(&mut self) -> Option { let c = self.source[self.pos]; match c { b' ' | b'\t' | b'\r' | b'\n' => { self.scan(|c| -> bool { matches!(c, b' ' | b'\t' | b'\r' | b'\n') }); Some(Token::Whitespace) } _ => { let mut len = 1; let mut utf8_step = 1; if c < 0x80 { len = 1; } else if c < 0xC0 { len = 1; } else if c < 0xE0 { len = 2; } else if c < 0xF0 { len = 3; } else if c < 0xF8 { len = 4; utf8_step = 2; } if len > 0 { let start = self.pos; let utf16_pos = self.utf16_pos; let buffer = &self.source[self.pos..(self.pos + len)]; self.forward(len); self.utf16_pos = utf16_pos + utf8_step; Some(Token::Cluster(buffer)) } else { None } } } } fn forward(&mut self, pos: usize) { self.pos += pos; } fn search(&mut self, test: FF) -> &'a str where FF: Fn(u8) -> bool, { let start = self.pos; self.scan(test); self.since(start) } fn since(&mut self, start: usize) -> &'a str { std::str::from_utf8(&self.source[start..self.pos]).unwrap() } fn scan(&mut self, test: FF) where FF: Fn(u8) -> bool, { while self.pos < self.source.len() { let c = self.source[self.pos]; self.pos += 1; if !test(c) { self.pos -= 1; break; } } } pub fn new(source: &'a str) -> Tokenizer { Tokenizer { source: source.as_bytes(), pos: 0, utf16_pos: 0, } } } ================================================ FILE: packages/paperclip-core/native/src/base/utils.rs ================================================ use crc::crc32; use regex::Regex; pub fn get_document_id<'a>(uri: &String) -> String { let mut buff = uri.clone(); if !uri.starts_with("file://") { buff = format!("file://{}", buff); } format!("{:x}", crc32::checksum_ieee(buff.as_bytes())).to_string() } pub fn get_document_style_public_scope<'a>(uri: &String) -> String { let mut buff = uri.clone(); if !uri.starts_with("file://") { buff = format!("file://{}", buff); } format!("pub-{:x}", crc32::checksum_ieee(buff.as_bytes())).to_string() } pub fn is_relative_path(path: &String) -> bool { let url_re = Regex::new(r"(https?|data):").unwrap(); return !url_re.is_match(path.clone().as_str()); } ================================================ FILE: packages/paperclip-core/native/src/core/ast.rs ================================================ use crate::base::ast::Range; use crate::css::ast as css_ast; use crate::pc::ast as pc_ast; use crate::script::ast as script_ast; use serde::Serialize; pub trait ExprVisitor<'a> { fn visit_node(&mut self, node: &'a pc_ast::Node); fn visit_attr(&mut self, node: &'a pc_ast::Attribute); fn visit_str(&mut self, node: &'a StringLiteral); fn visit_css_rule(&mut self, rule: &'a css_ast::Rule); fn visit_css_sheet(&mut self, rule: &'a css_ast::Sheet); fn visit_css_decl(&mut self, rule: &'a css_ast::Declaration); fn visit_script_expression(&mut self, rule: &'a script_ast::Expression); fn should_continue(&self) -> bool; } pub fn walk_exprs<'a, TExpr: Expr>(exprs: &'a Vec, visitor: &mut ExprVisitor<'a>) { for expr in exprs { expr.walk(visitor); if !visitor.should_continue() { break; } } } pub trait Expr: std::fmt::Debug { fn walk<'a>(&'a self, visitor: &mut ExprVisitor<'a>); fn get_id<'a>(&'a self) -> &'a String; fn wrap<'a>(&'a self) -> pc_ast::Expression<'a>; } pub fn find_expr_by_id<'a>(target_id: String, ast: &'a Expr) -> Option> { ExprByIdFinder::find(target_id, ast) } struct ExprByIdFinder<'a> { target_id: String, found_expr: Option>, } impl<'a> ExprByIdFinder<'a> { fn new(target_id: String) -> ExprByIdFinder<'a> { ExprByIdFinder { target_id, found_expr: None, } } fn find(target_id: String, ast: &'a Expr) -> Option> { let mut visitor = ExprByIdFinder::new(target_id); ast.walk(&mut visitor); visitor.found_expr } pub fn visit_core_expr(&mut self, expr: &'a Expr) { if expr.get_id() == &self.target_id { self.found_expr = Some(expr.wrap()); } } } impl<'a> ExprVisitor<'a> for ExprByIdFinder<'a> { fn visit_node(&mut self, expr: &'a pc_ast::Node) { self.visit_core_expr(expr); } fn visit_attr(&mut self, expr: &'a pc_ast::Attribute) { self.visit_core_expr(expr); } fn visit_str(&mut self, expr: &'a StringLiteral) { self.visit_core_expr(expr); } fn visit_css_rule(&mut self, expr: &'a css_ast::Rule) { self.visit_core_expr(expr); } fn visit_css_decl(&mut self, expr: &'a css_ast::Declaration) { self.visit_core_expr(expr); } fn visit_css_sheet(&mut self, expr: &'a css_ast::Sheet) { self.visit_core_expr(expr); } fn visit_script_expression(&mut self, expr: &'a script_ast::Expression) { self.visit_core_expr(expr); } fn should_continue(&self) -> bool { return self.found_expr == None; } } pub enum CoreExpression { StringLiteral(StringLiteral), } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct StringLiteral { pub id: String, pub value: String, pub range: Range, } impl Expr for StringLiteral { fn walk<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { visitor.visit_str(self); } fn get_id<'a>(&'a self) -> &'a String { &self.id } fn wrap<'a>(&'a self) -> pc_ast::Expression<'a> { pc_ast::Expression::String(self) } } ================================================ FILE: packages/paperclip-core/native/src/core/diagnostics.rs ================================================ /* Diagnostics are error types for helping the user debug their program. Anything that consumes PC code should report diagnostics. All other parts of the app can use regular error handling. */ use crate::base::ast::ExprTextSource; use crate::base::ast::Range; use serde::Serialize; #[derive(Debug, PartialEq, Serialize, Clone)] pub enum DiagnosticLevel { Warning, Error, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct DiagnosticSourceInfo { // source where the error is. Might not exist #[serde(rename = "sourceId")] expression_id: String, // This may not exist if we're dealing with just the AST #[serde(rename = "textSource")] text_source: Option, } impl DiagnosticSourceInfo { pub fn new<'a>( expression_id: &'a str, text_source: Option<&ExprTextSource>, ) -> DiagnosticSourceInfo { DiagnosticSourceInfo { expression_id: expression_id.to_string(), text_source: text_source.and_then(|text_source| Some(text_source.clone())), } } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct FileNotFoundInfo { uri: String, } #[derive(Debug, PartialEq, Serialize, Clone)] pub enum SyntaxDiagnosticInfoCode { Unknown, EndOfFile, Unexpected, Unterminated, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "syntaxDiagnosticInfoKind")] pub struct SyntaxDiagnosticInfo { source: ExprTextSource, code: SyntaxDiagnosticInfoCode, } impl SyntaxDiagnosticInfo { pub fn new_error_dignostic<'a>( code: SyntaxDiagnosticInfoCode, message: &'a str, uri: &'a str, start: usize, end: usize, ) -> Diagnostic { Diagnostic::new_error( message, DiagnosticInfo::Syntax(SyntaxDiagnosticInfo { code, source: ExprTextSource::new(uri.to_string(), Range::new(start, end)), }), ) } pub fn new_unexpected_token_error_diagnostic<'a>(uri: &'a str, start: usize) -> Diagnostic { SyntaxDiagnosticInfo::new_error_dignostic( SyntaxDiagnosticInfoCode::Unexpected, "Unexpected token", uri, start, start + 1, ) } pub fn new_unknown_error_diagnostic<'a>(uri: &'a str) -> Diagnostic { SyntaxDiagnosticInfo::new_error_dignostic( SyntaxDiagnosticInfoCode::Unknown, "Unknown", uri, 0, 1, ) } pub fn new_eof_error_diagnostic<'a>(uri: &'a str) -> Diagnostic { SyntaxDiagnosticInfo::new_error_dignostic( SyntaxDiagnosticInfoCode::Unknown, "End of file", uri, 0, 1, ) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ImportNotFoundInfo { source: ExprTextSource, link_uri: String, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "kind")] pub enum DiagnosticInfo { ImportNotFound(ImportNotFoundInfo), Syntax(SyntaxDiagnosticInfo), UnableToParseFile(DiagnosticSourceInfo), FileNotFound(FileNotFoundInfo), UnusedStyleRule(DiagnosticSourceInfo), CircularDependencyDetected(ExprTextSource), DependencyNotLoaded(ExprTextSource), IncorectFileType(ExprTextSource), } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Diagnostic { // level of warning level: DiagnosticLevel, info: DiagnosticInfo, // message to the user message: String, } impl Diagnostic { pub fn new<'a>(message: &'a str, level: DiagnosticLevel, info: DiagnosticInfo) -> Diagnostic { Diagnostic { message: message.to_string(), level, info, } } pub fn new_warning<'a>(message: &'a str, info: DiagnosticInfo) -> Diagnostic { Diagnostic::new(message, DiagnosticLevel::Warning, info) } pub fn new_error<'a>(message: &'a str, info: DiagnosticInfo) -> Diagnostic { Diagnostic::new(message, DiagnosticLevel::Error, info) } } ================================================ FILE: packages/paperclip-core/native/src/core/eval.rs ================================================ use super::vfs::VirtualFileSystem; use crate::base::parser::ParseError; // use crate::css::runtime::exports as css_exports; // use crate::pc::runtime::exports as pc_exports; use crate::css::runtime::evaluator as css_eval; use crate::pc::runtime::evaluator as pc_eval; use serde::Serialize; use std::collections::{BTreeMap, HashSet}; // #[derive(Debug, PartialEq, Clone, Serialize)] // pub enum DependencyExport { // CSS(css_exports::Exports), // PC(css_exports::Exports) // } #[derive(Debug, PartialEq, Clone, Serialize)] #[serde(tag = "kind")] pub enum DependencyEvalInfo { CSS(css_eval::EvalInfo), PC(pc_eval::EvalInfo), } ================================================ FILE: packages/paperclip-core/native/src/core/eval_utils.rs ================================================ use super::vfs::VirtualFileSystem; use crate::base::ast::Range; use crate::base::runtime::RuntimeError; use crate::base::utils::is_relative_path; pub fn resolve_asset( from_uri: &String, to_path: &String, range: &Range, vfs: &VirtualFileSystem, ) -> Result { if !is_relative_path(to_path) { return Ok(to_path.to_string()); } let value_option = vfs.resolve(from_uri, &to_path); if let Some(value) = &value_option { if vfs.file_exists(&value) { return Ok(value.to_string()); } } return Err(RuntimeError::new( format!("Unable to resolve file: {}", to_path), from_uri, range, )); } ================================================ FILE: packages/paperclip-core/native/src/core/graph.rs ================================================ use super::vfs::VirtualFileSystem; use crate::base::ast::Range; use crate::base::parser::ParseError; use crate::base::utils::get_document_id; use crate::core::ast::find_expr_by_id; use crate::css::{ast as css_ast, parser as css_parser}; use crate::pc::{ast as pc_ast, parser as pc_parser}; use serde::Serialize; use std::collections::{BTreeMap, HashSet}; #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "kind")] pub enum GraphErrorInfo { // , , IncludeNotFound(IncludeNodeFoundError), Syntax(ParseError), NotFound, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct IncludeNodeFoundError { pub uri: String, pub range: Range, pub message: String, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct GraphError { uri: String, info: GraphErrorInfo, } #[derive(Debug, Clone)] pub struct DependencyGraph { pub dependencies: BTreeMap, } pub enum DependencyObject<'a> { Dependency(&'a Dependency), Expression(pc_ast::Expression<'a>), } #[allow(dead_code)] impl DependencyGraph { pub fn new() -> DependencyGraph { DependencyGraph { dependencies: BTreeMap::new(), } } pub fn flatten<'a>(&'a self, entry_uri: &String) -> Vec<(&Dependency, Option<&Dependency>)> { let mut deps: Vec<(&Dependency, Option<&Dependency>)> = vec![]; if !self.dependencies.contains_key(entry_uri) { return deps; } let entry = self.dependencies.get(entry_uri).unwrap(); deps.push((entry, None)); let mut dependents = vec![entry]; while dependents.len() > 0 { let dependent = dependents.pop().unwrap(); for (_, dep_uri) in &dependent.dependencies { if let Some(dep) = self.dependencies.get(dep_uri) { deps.push((dep, Some(dependent))); dependents.push(dep); } } } return deps; } pub fn flatten_dependents<'a>(&'a self, entry_uri: &String) -> Vec { let mut all_deps: HashSet = HashSet::new(); self.flatten_dependents2(entry_uri, &mut all_deps); return all_deps .into_iter() .map(|v| v.to_string()) .collect::>(); } pub fn get_expression_by_id<'a>( &'a self, source_id: &String, ) -> Option<(String, pc_ast::Expression<'a>)> { for (uri, dep) in self.dependencies.iter() { let option: Option> = dep.get_expression_by_id(source_id); if let Some(obj) = option { return Some((uri.to_string(), obj)); } } None } fn flatten_dependents2<'a>(&'a self, entry_uri: &String, all_deps: &mut HashSet) { let entry_option = self.dependencies.get(entry_uri); if let None = entry_option { return; } for (dep_uri, dep) in &self.dependencies { if dep.dependencies.values().any(|uri| &uri == &entry_uri) && !all_deps.contains(dep_uri) { all_deps.insert(dep_uri.to_string()); self.flatten_dependents2(dep_uri, all_deps); } else if all_deps.contains(dep_uri) { } } return; } pub fn get_object_by_id<'a>(&'a self, id: &String) -> Option<(String, DependencyObject<'a>)> { for (uri, dependency) in &self.dependencies { if &dependency.get_id() == id { return Some((uri.to_string(), DependencyObject::Dependency(&dependency))); } } self .get_expression_by_id(id) .and_then(|(uri, object)| return Some((uri, DependencyObject::Expression(object)))) } pub fn flatten_dependencies<'a>(&'a self, entry_uri: &String) -> Vec { let mut all_deps: Vec = vec![]; self.flatten_dependencies2(entry_uri, &mut all_deps); return all_deps; } fn flatten_dependencies2<'a>(&'a self, entry_uri: &String, all_deps: &mut Vec) { let entry_option = self.dependencies.get(entry_uri); if let Some(dep) = entry_option { let deps = dep .sorted_import_ids .iter() .map(|(src)| dep.dependencies.get(src).unwrap()) .collect::>(); for dep_uri in deps { if !all_deps.contains(dep_uri) { self.flatten_dependencies2(dep_uri, all_deps); // this needs to be added _after_ so that it gets higher priority all_deps.push(dep_uri.to_string()); } else { } } } } pub async fn load_dependency<'a>( &mut self, uri: &String, vfs: &mut VirtualFileSystem, ) -> Result, GraphError> { let mut loaded_deps = vec![]; loaded_deps.push(uri.to_string()); let mut to_load: Vec<(String, Option<(String, String)>)> = vec![(uri.to_string(), None)]; while to_load.len() > 0 { let (curr_uri, import) = to_load.pop().unwrap(); let source = vfs .load(&curr_uri) .await .or_else(|_| { let err: GraphError = match import { Some((origin_uri, relative_uri)) => { if let Some(origin_dep) = self.dependencies.get(&origin_uri) { let range = match &origin_dep.content { DependencyContent::Node(node) => pc_ast::get_import_by_src(&relative_uri, node) .unwrap() .open_tag_range .clone(), DependencyContent::StyleSheet(_) => { // TODO once imports are working in CSS sheets Range::nil() } }; let info = GraphErrorInfo::IncludeNotFound(IncludeNodeFoundError { message: "import not found".to_string(), uri: curr_uri.to_string(), range, }); GraphError { uri: origin_uri.to_string(), info, } } else { GraphError { uri: curr_uri.to_string(), info: GraphErrorInfo::NotFound, } } } None => GraphError { uri: curr_uri.to_string(), info: GraphErrorInfo::NotFound, }, }; Err(err) })? .to_string(); // TODO - check if content matches old content. let dependency_option = Dependency::from_source(source, &curr_uri, vfs, get_document_id(&curr_uri).as_str()) .or_else(|error| { Err(GraphError { uri: curr_uri.to_string(), info: GraphErrorInfo::Syntax(error), }) }); match dependency_option { Ok(dependency) => { loaded_deps.push(curr_uri.to_string()); self.dependencies.insert(curr_uri.to_string(), dependency); // need to insert now for let dep = &self.dependencies.get(&curr_uri).unwrap(); for (relative_uri, dep_uri) in &dep.dependency_uri_maps { if !self.dependencies.contains_key(&dep_uri.to_string()) { to_load.push(( dep_uri.to_string(), Some((curr_uri.to_string(), relative_uri.to_string())), )); } } } Err(err) => { self.dependencies.remove(&curr_uri); return Err(err); } } } Ok(loaded_deps) } } #[derive(Debug, Serialize, Clone)] #[serde(tag = "contentKind")] pub enum DependencyContent { Node(pc_ast::Node), StyleSheet(css_ast::Sheet), } #[derive(Debug, Serialize, Clone)] pub struct Dependency { pub uri: String, #[serde(rename = "dependencies")] pub dependencies: BTreeMap, #[serde(rename = "dependencyUriMaps")] pub dependency_uri_maps: BTreeMap, // necessary since BTree maps don't sort based on insertion pub sorted_import_ids: Vec, pub content: DependencyContent, } impl<'a> Dependency { pub fn from_source<'b>( source: String, uri: &String, vfs: &VirtualFileSystem, id_seed: &'b str, ) -> Result { if uri.ends_with(".css") { Dependency::from_css_source(source, uri, id_seed) } else { Dependency::from_pc_source(source, uri, vfs, id_seed) } } pub fn get_expression_by_id(&'a self, source_id: &String) -> Option> { match &self.content { DependencyContent::StyleSheet(sheet) => find_expr_by_id(source_id.clone(), sheet), DependencyContent::Node(node) => find_expr_by_id(source_id.clone(), node), } } pub fn get_id(&self) -> String { get_document_id(&self.uri).to_string() } fn from_css_source<'b>( source: String, uri: &String, id_seed: &'b str, ) -> Result { let expression_result = css_parser::parse(&source, id_seed.to_string()); if let Err(err) = expression_result { return Err(err); } let expression = expression_result.unwrap(); Ok(Dependency { uri: uri.to_string(), content: DependencyContent::StyleSheet(expression), dependencies: BTreeMap::new(), dependency_uri_maps: BTreeMap::new(), sorted_import_ids: vec![], }) } fn from_pc_source<'b>( source: String, uri: &String, vfs: &VirtualFileSystem, id_seed: &'b str, ) -> Result { let expression_result = pc_parser::parse(source.as_str(), uri.as_str(), id_seed); if let Err(err) = expression_result { return Err(err); } let expression = expression_result.unwrap(); let imports = pc_ast::get_imports(&expression); let mut dependencies = BTreeMap::new(); let mut dependency_uri_maps = BTreeMap::new(); let mut sorted_import_ids = vec![]; for import in &imports { let src = pc_ast::get_attribute_value("src", import).unwrap(); let resolved_src_option = vfs.resolve(uri, &src); if let Some(resolved_src) = resolved_src_option { let import_id = pc_ast::get_import_identifier(import) .unwrap() .as_str() .to_string(); dependencies.insert(import_id.to_string(), resolved_src.to_string()); sorted_import_ids.push(import_id.to_string()); dependency_uri_maps.insert(src.to_string(), resolved_src.to_string()); } else { return Err(ParseError::unexpected( format!("Unable to resolve path: {} from {}", src, uri), Range::nil(), )); } } Ok(Dependency { uri: uri.to_string(), content: DependencyContent::Node(expression), sorted_import_ids, dependencies, dependency_uri_maps, }) } } ================================================ FILE: packages/paperclip-core/native/src/core/id_generator.rs ================================================ use uuid::Uuid; #[derive(Debug, Clone)] pub struct IDGenerator { pub seed: String, pub count: u32, } impl IDGenerator { pub fn new(seed: String) -> IDGenerator { IDGenerator { seed, count: 0 } } pub fn new_id(&mut self) -> String { let id_count = self.count + 1; self.count = id_count; format!("{}-{}", self.seed, id_count) } pub fn new_seed(&mut self) -> String { self.new_id() } } pub fn generate_seed() -> String { Uuid::new_v4().to_string() } ================================================ FILE: packages/paperclip-core/native/src/core/mod.rs ================================================ // pub mod diagnostics; pub mod ast; pub mod eval; pub mod eval_utils; pub mod graph; pub mod id_generator; pub mod vfs; ================================================ FILE: packages/paperclip-core/native/src/core/vfs.rs ================================================ use std::collections::HashMap; // use curl::easy::Easy; pub type FileReaderFn = dyn Fn(&String) -> String; pub type FileExistsFn = dyn Fn(&String) -> bool; pub type FileResolverFn = dyn Fn(&String, &String) -> Option; #[allow(dead_code)] pub struct VirtualFileSystem { read_file: Box, resolve_file: Box, file_exists: Box, pub contents: HashMap, } #[allow(dead_code)] impl VirtualFileSystem { pub fn new( read_file: Box, file_exists: Box, resolve_file: Box, ) -> VirtualFileSystem { VirtualFileSystem { read_file, file_exists, resolve_file, contents: HashMap::new(), } } pub fn reset(&mut self) { self.contents = HashMap::new(); } pub async fn load(&mut self, uri: &String) -> Result<&String, &'static str> { if self.contents.contains_key(uri) { Ok(self.contents.get(uri).unwrap()) } else { self.reload(uri).await } } pub fn file_exists(&self, uri: &String) -> bool { (self.file_exists)(uri) } pub fn resolve(&self, from_path: &String, relative_path: &String) -> Option { (self.resolve_file)(from_path, relative_path) } pub fn purge_unlinked_files(&mut self) -> Vec { let mut unlinked: Vec = Vec::new(); for (uri, _) in self.contents.iter() { if !self.file_exists(uri) { unlinked.push(uri.clone()); } } for uri in &unlinked { self.contents.remove(uri); } unlinked } pub async fn update(&mut self, uri: &String, content: &String) -> Result<&String, &'static str> { if !self.file_exists(uri) { return Err("File does not exist"); } self.contents.insert(uri.to_string(), content.to_string()); Ok(self.contents.get(uri).unwrap()) } pub async fn reload(&mut self, uri: &String) -> Result<&String, &'static str> { if !self.file_exists(uri) { return Err("File does not exist"); } let content = (self.read_file)(uri); self.contents.insert(uri.to_string(), content); Ok(self.contents.get(uri).unwrap()) } } ================================================ FILE: packages/paperclip-core/native/src/coverage/mod.rs ================================================ pub mod reporter; ================================================ FILE: packages/paperclip-core/native/src/coverage/reporter.rs ================================================ use crate::base::ast as base_ast; use crate::core::ast as core_ast; use crate::core::ast::{Expr, ExprVisitor}; use crate::core::eval::DependencyEvalInfo; use crate::core::graph::{Dependency, DependencyContent, DependencyGraph}; use crate::core::vfs::VirtualFileSystem; use crate::css::ast as css_ast; use crate::css::runtime::evaluator as css_eval; use crate::pc::ast as pc_ast; use crate::pc::runtime::evaluator as pc_eval; use crate::pc::runtime::inspect_node_styles::{inspect_node_styles, InspectionOptions}; use crate::pc::runtime::virt as pc_virt; use crate::script::ast as script_ast; use futures::future::{join_all, BoxFuture}; use serde::Serialize; use std::collections::hash_map::Entry; use std::collections::{BTreeMap, HashMap, HashSet}; use std::io::{BufRead, Cursor}; #[derive(Debug, PartialEq, Serialize)] struct CoverageSummary { // 0 <> 100 statements: u32, // 0 <> 100 lines: u32, } #[derive(Clone, Hash, Debug, Eq, PartialEq)] enum ExprIdKind { CSS, HTML, } #[derive(Clone, Debug, PartialEq)] struct ExprIdInfo { source_uri: String, kind: ExprIdKind, } #[derive(Debug, PartialEq, Serialize)] pub struct CoverageReport { files: Vec, } #[derive(Clone, Debug, PartialEq, Serialize)] pub struct PartReport { #[serde(rename = "missingRanges")] missing_ranges: Vec, count: u32, } struct ExprCounts { css: u32, html: u32, } #[derive(Debug, PartialEq, Serialize)] struct FileReport { uri: String, css: PartReport, html: PartReport, #[serde(rename = "missingLines")] missing_lines: HashSet, #[serde(rename = "lineCount")] line_count: u32, } struct Source<'a> { graph: &'a DependencyGraph, evaluated: &'a BTreeMap, } struct AnalyzeContext<'a> { pub evaluated: &'a BTreeMap, pub missed_ids: HashMap, pub graph: &'a DependencyGraph, } pub async fn generate_coverage_report( graph: &DependencyGraph, evaluated: &BTreeMap, vfs: &mut VirtualFileSystem, ) -> CoverageReport { let source: Source = Source { graph, evaluated }; let coverable_expr_ids = get_coverable_expr_ids(&source); let expr_counts = count_exprs(&coverable_expr_ids); let mut analyze_context = AnalyzeContext { missed_ids: coverable_expr_ids, evaluated, graph, }; analyze_graph(&mut analyze_context); CoverageReport { files: generate_file_reports(&analyze_context.missed_ids, &expr_counts, graph, vfs).await, } } fn count_exprs(expr_ids: &HashMap) -> HashMap { let mut expr_counts: HashMap = HashMap::new(); for (id, info) in expr_ids { let mut counts = match expr_counts.entry(info.source_uri.to_string()) { Entry::Occupied(e) => e.into_mut(), Entry::Vacant(e) => e.insert(ExprCounts { css: 0, html: 0 }), }; match info.kind { ExprIdKind::CSS => counts.css = counts.css + 1, ExprIdKind::HTML => counts.html = counts.html + 1, } } expr_counts } async fn generate_file_reports( missed_ids: &HashMap, expr_counts: &HashMap, graph: &DependencyGraph, vfs: &mut VirtualFileSystem, ) -> Vec { let mut reports: Vec> = vec![]; let mut file_expr_id_map: HashMap> = HashMap::new(); for (id, info) in missed_ids { if let Some(file_ids) = file_expr_id_map.get_mut(&info.source_uri) { file_ids.insert(id.to_string(), info.clone()); } else { let mut ids = HashMap::new(); ids.insert(id.to_string(), info.clone()); file_expr_id_map.insert(info.source_uri.to_string(), ids); } } let mut contents: HashMap = HashMap::new(); for (uri, _) in &graph.dependencies { contents.insert(uri.to_string(), vfs.load(uri).await.unwrap().to_string()); } for (uri, dep) in &graph.dependencies { reports.push(Box::pin(generate_file_report( &uri, contents.remove(uri).unwrap(), dep, expr_counts.get(uri).unwrap(), file_expr_id_map.remove(uri).unwrap_or(HashMap::new()), ))); } join_all(reports).await } async fn generate_file_report( uri: &String, content: String, dep: &Dependency, counts: &ExprCounts, missing_ids: HashMap, ) -> FileReport { let mut missing_parts: HashMap = HashMap::new(); let mut missing_statement_ranges: Vec = vec![]; missing_parts.insert( ExprIdKind::HTML, PartReport { count: counts.html, missing_ranges: vec![], }, ); missing_parts.insert( ExprIdKind::CSS, PartReport { count: counts.css, missing_ranges: vec![], }, ); for (id, info) in &missing_ids { if let Some(expr) = dep.get_expression_by_id(id) { if let Some(part_report) = missing_parts.get_mut(&info.kind) { part_report.missing_ranges.push(expr.get_range().clone()); missing_statement_ranges.push(expr.get_range().clone()); } } else { panic!("Expr ID not found"); } } let line_count = count_lines(&content); FileReport { uri: uri.to_string(), missing_lines: calc_missed_lines(&missing_statement_ranges), line_count, css: missing_parts .get(&ExprIdKind::CSS) .unwrap_or(&PartReport { count: 0, missing_ranges: vec![], }) .clone(), html: missing_parts .get(&ExprIdKind::HTML) .unwrap_or(&PartReport { count: 0, missing_ranges: vec![], }) .clone(), } } fn calc_missed_lines(mised_statement_ranges: &Vec) -> HashSet { let mut missed_lines: HashSet = HashSet::new(); for range in mised_statement_ranges { for i in range.start.line..(range.end.line + 1) { missed_lines.insert(i as u32); } } missed_lines } fn count_lines(content: &String) -> u32 { let cursor = Cursor::new(content.as_bytes()); let mut lc = 0; for _line in cursor.lines() { lc = lc + 1; } lc } fn get_coverable_expr_ids(source: &Source) -> HashMap { let mut missed: HashMap = HashMap::new(); for (uri, dep) in &source.graph.dependencies { for (id, kind) in get_missed_file_expr_ids(dep) { missed.insert( id.to_string(), ExprIdInfo { source_uri: uri.to_string(), kind, }, ); } } missed } fn get_missed_file_expr_ids(dep: &Dependency) -> HashMap { match &dep.content { DependencyContent::Node(node) => ExprIdCollector::collect_expr_ids(node), DependencyContent::StyleSheet(sheet) => ExprIdCollector::collect_expr_ids(sheet), } } fn analyze_graph(context: &mut AnalyzeContext) { for (uri, evaluated) in context.evaluated { analyze_content(uri, evaluated, context); } } fn analyze_content(uri: &String, content: &DependencyEvalInfo, context: &mut AnalyzeContext) { match content { DependencyEvalInfo::CSS(css) => { analyze_css_content(uri, css, context); } DependencyEvalInfo::PC(pc) => { analyze_pc_content(uri, pc, context); } } } fn analyze_css_content( _uri: &String, _content: &css_eval::EvalInfo, _context: &mut AnalyzeContext, ) { } fn analyze_pc_content(uri: &String, content: &pc_eval::EvalInfo, context: &mut AnalyzeContext) { // first, remove all PC expressions that have been used. This includes scripts, and HTML if let Some(used_expr_ids) = &content.used_expr_ids { for used_id in used_expr_ids { context.missed_ids.remove(used_id); } } analayze_node_styles(uri, &content.preview, &vec![], context); } fn analayze_node_styles( uri: &String, node: &pc_virt::Node, path: &Vec, context: &mut AnalyzeContext, ) { if let pc_virt::Node::Element(element) = node { analayze_element_styles(uri, path, context); } if let Some(children) = node.get_children() { for (index, child) in children.iter().enumerate() { let mut child_path = path.clone(); child_path.push(index); analayze_node_styles(uri, &child, &child_path, context); } } } fn analayze_element_styles(uri: &String, path: &Vec, context: &mut AnalyzeContext) { let inspection = inspect_node_styles( path, uri, context.evaluated, &context.graph, &InspectionOptions { screen_width: None, // omit things like font-family and such. We just want rules that are // attached to the target node include_inherited: false, }, ); for rule in inspection.style_rules { context.missed_ids.remove(&rule.source_id); } } struct ExprIdCollector { pub expr_ids: HashMap, } impl ExprIdCollector { fn visit_core_expr<'a>(&'a mut self, expr: &'a dyn Expr, kind: ExprIdKind) { self.expr_ids.insert(expr.get_id().to_string(), kind); } fn collect_expr_ids(expr: &dyn Expr) -> HashMap { let mut collector = ExprIdCollector { expr_ids: HashMap::new(), }; expr.walk(&mut collector); collector.expr_ids } } impl<'a> ExprVisitor<'a> for ExprIdCollector { fn visit_node(&mut self, expr: &'a pc_ast::Node) { match expr { pc_ast::Node::Comment(_) => {} _ => { self.visit_core_expr(expr, ExprIdKind::HTML); } }; } fn visit_attr(&mut self, expr: &'a pc_ast::Attribute) { let attr_name = match expr { pc_ast::Attribute::KeyValueAttribute(kv) => kv.name.to_string(), pc_ast::Attribute::PropertyBoundAttribute(kv) => kv.name.to_string(), pc_ast::Attribute::ShorthandAttribute(kv) => kv.reference.to_string(), pc_ast::Attribute::SpreadAttribute(kv) => kv.script.to_string(), }; // skip event handlers. This is a dirty approach if attr_name.starts_with("on") { return; } self.visit_core_expr(expr, ExprIdKind::HTML); } fn visit_css_rule(&mut self, expr: &'a css_ast::Rule) { match expr { css_ast::Rule::Style(_) => { self.visit_core_expr(expr, ExprIdKind::CSS); } _ => {} } } fn visit_css_decl(&mut self, _expr: &'a css_ast::Declaration) { // noop } fn visit_css_sheet(&mut self, _expr: &'a css_ast::Sheet) { // noop } fn visit_str(&mut self, _expr: &'a core_ast::StringLiteral) { // noop } fn visit_script_expression(&mut self, expr: &'a script_ast::Expression) { self.visit_core_expr(expr, ExprIdKind::HTML); } fn should_continue(&self) -> bool { return true; } } #[cfg(test)] mod tests { use super::*; use crate::base::string_scanner::U16Position; use crate::engine::engine::Engine; use crate::engine::test_utils::create_mock_engine; use ::futures::executor::block_on; use std::iter::FromIterator; fn open_all_entries(graph: &HashMap, engine: &mut Engine) { for (uri, _) in graph { if let Err(err) = block_on(engine.run(uri)) { panic!("{:?}", err); } } } #[test] fn can_get_basic_coverage_on_a_node() { let graph: HashMap = [("entry.pc".to_string(), "
    ".to_string())] .iter() .cloned() .collect(); assert_graph_report( graph, CoverageReport { files: vec![FileReport { uri: "entry.pc".to_string(), missing_lines: HashSet::from_iter(vec![].iter().cloned()), line_count: 1, css: PartReport { count: 0, missing_ranges: vec![], }, html: PartReport { count: 2, missing_ranges: vec![], }, }], }, ); } #[test] fn doesnt_show_missing_lines_for_slot_element() { let graph: HashMap = [( "entry.pc".to_string(), "
    {
    }
    " .to_string(), )] .iter() .cloned() .collect(); assert_graph_report( graph, CoverageReport { files: vec![FileReport { uri: "entry.pc".to_string(), missing_lines: HashSet::from_iter(vec![].iter().cloned()), line_count: 7, css: PartReport { count: 0, missing_ranges: vec![], }, html: PartReport { count: 7, missing_ranges: vec![], }, }], }, ); } #[test] fn shows_missing_statements_for_a_slot() { let graph: HashMap = [( "entry.pc".to_string(), "
    {a &&
    }
    " .to_string(), )] .iter() .cloned() .collect(); assert_graph_report( graph, CoverageReport { files: vec![FileReport { uri: "entry.pc".to_string(), missing_lines: HashSet::from_iter(vec![3].iter().cloned()), line_count: 7, css: PartReport { count: 0, missing_ranges: vec![], }, html: PartReport { count: 9, missing_ranges: vec![base_ast::Range::new( U16Position::new(51, 3, 17), U16Position::new(58, 3, 24), )], }, }], }, ); } #[test] fn shows_other_script_expressions_as_covered() { let graph: HashMap = [( "entry.pc".to_string(), "
    {1} {a ||
    } {(true)} {\"a\"} {!true}
    " .to_string(), )] .iter() .cloned() .collect(); assert_graph_report( graph, CoverageReport { files: vec![FileReport { uri: "entry.pc".to_string(), missing_lines: HashSet::from_iter(vec![].iter().cloned()), line_count: 11, css: PartReport { count: 0, missing_ranges: vec![], }, html: PartReport { count: 18, missing_ranges: vec![], }, }], }, ); } #[test] fn shows_element_as_covered_if_variant_provided() { let graph: HashMap = [( "entry.pc".to_string(), "
    {a &&
    }
    " .to_string(), )] .iter() .cloned() .collect(); assert_graph_report( graph, CoverageReport { files: vec![FileReport { uri: "entry.pc".to_string(), missing_lines: HashSet::from_iter(vec![].iter().cloned()), line_count: 8, css: PartReport { count: 0, missing_ranges: vec![], }, html: PartReport { count: 11, missing_ranges: vec![], }, }], }, ); } #[test] fn variant_class_names_that_arent_used_are_shown_as_missing() { let graph: HashMap = [( "entry.pc".to_string(), "
    " .to_string(), )] .iter() .cloned() .collect(); assert_graph_report( graph, CoverageReport { files: vec![FileReport { uri: "entry.pc".to_string(), missing_lines: HashSet::from_iter(vec![2].iter().cloned()), line_count: 6, html: PartReport { count: 7, missing_ranges: vec![base_ast::Range { start: U16Position { pos: 41, line: 2, column: 41, }, end: U16Position { pos: 58, line: 2, column: 58, }, }], }, css: PartReport { count: 0, missing_ranges: vec![], }, }], }, ); } #[test] fn variant_class_names_that_are_used_are_not_reported_as_missing() { let graph: HashMap = [( "entry.pc".to_string(), "
    " .to_string(), )] .iter() .cloned() .collect(); assert_graph_report( graph, CoverageReport { files: vec![FileReport { uri: "entry.pc".to_string(), missing_lines: HashSet::from_iter(vec![].iter().cloned()), line_count: 6, html: PartReport { count: 8, missing_ranges: vec![], }, css: PartReport { count: 0, missing_ranges: vec![], }, }], }, ); } #[test] fn shows_style_as_covered() { let graph: HashMap = [( "entry.pc".to_string(), "
    " .to_string(), )] .iter() .cloned() .collect(); assert_graph_report( graph, CoverageReport { files: vec![FileReport { uri: "entry.pc".to_string(), missing_lines: HashSet::from_iter(vec![].iter().cloned()), line_count: 13, html: PartReport { count: 3, missing_ranges: vec![], }, css: PartReport { count: 2, missing_ranges: vec![], }, }], }, ); } #[test] fn shows_style_as_not_covered_if_not_applied() { let graph: HashMap = [( "entry.pc".to_string(), "
    " .to_string(), )] .iter() .cloned() .collect(); assert_graph_report( graph, CoverageReport { files: vec![FileReport { uri: "entry.pc".to_string(), missing_lines: HashSet::from_iter(vec![4, 6, 3, 5].iter().cloned()), line_count: 8, css: PartReport { count: 1, missing_ranges: vec![base_ast::Range { start: U16Position { pos: 27, line: 3, column: 11, }, end: U16Position { pos: 79, line: 6, column: 9, }, }], }, html: PartReport { count: 3, missing_ranges: vec![], }, }], }, ); } #[test] fn shows_external_file_as_covered() { let graph: HashMap = [ ( "entry.pc".to_string(), " " .to_string(), ), ( "atoms.pc".to_string(), " " .to_string(), ), ] .iter() .cloned() .collect(); assert_graph_report( graph, CoverageReport { files: vec![ FileReport { uri: "atoms.pc".to_string(), missing_lines: HashSet::from_iter(vec![7, 8, 9, 10].iter().cloned()), line_count: 12, css: PartReport { count: 2, missing_ranges: vec![base_ast::Range { start: U16Position { pos: 109, line: 7, column: 13, }, end: U16Position { pos: 140, line: 10, column: 11, }, }], }, html: PartReport { count: 2, missing_ranges: vec![], }, }, FileReport { uri: "entry.pc".to_string(), missing_lines: HashSet::from_iter(vec![].iter().cloned()), line_count: 4, css: PartReport { count: 0, missing_ranges: vec![], }, html: PartReport { count: 5, missing_ranges: vec![], }, }, ], }, ); } #[test] fn shows_instances_of_instances_covered() { let graph: HashMap = [( "entry.pc".to_string(), "
    " .to_string(), )] .iter() .cloned() .collect(); assert_graph_report( graph, CoverageReport { files: vec![FileReport { uri: "entry.pc".to_string(), missing_lines: HashSet::from_iter(vec![].iter().cloned()), line_count: 17, html: PartReport { count: 12, missing_ranges: vec![], }, css: PartReport { count: 0, missing_ranges: vec![], }, }], }, ); } fn assert_graph_report(graph: HashMap, report: CoverageReport) { let mut engine = create_mock_engine(&graph); open_all_entries(&graph, &mut engine); assert_eq!(block_on(engine.generate_coverage_report()), Ok(report)); } } ================================================ FILE: packages/paperclip-core/native/src/css/ast.rs ================================================ use crate::base::ast::{BasicRaws, Range}; use crate::core::ast as core_ast; use crate::core::ast::{Expr, ExprVisitor}; use crate::pc::ast as pc_ast; use serde::Serialize; use std::fmt; #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "declarationKind")] pub enum Declaration { KeyValue(KeyValueDeclaration), Include(Include), Content(Content), Media(ConditionRule), } impl Declaration { pub fn get_range(&self) -> &Range { match self { Declaration::KeyValue(kv) => &kv.range, Declaration::Include(kv) => &kv.range, Declaration::Content(kv) => &kv.range, Declaration::Media(kv) => &kv.range, } } } impl Expr for Declaration { fn walk<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { visitor.visit_css_decl(self); } fn get_id<'a>(&'a self) -> &'a String { match self { Declaration::KeyValue(kv) => &kv.id, Declaration::Include(kv) => &kv.id, Declaration::Content(kv) => &kv.id, Declaration::Media(kv) => &kv.id, } } fn wrap<'a>(&'a self) -> pc_ast::Expression<'a> { return pc_ast::Expression::CSS(Expression::Declaration(self)); } } impl fmt::Display for Declaration { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Declaration::KeyValue(kv) => kv.fmt(f), Declaration::Include(inc) => inc.fmt(f), Declaration::Media(media) => media.fmt(f), Declaration::Content(_) => writeln!(f, "@content;"), } } } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "cssObjectKind")] pub enum Expression<'a> { Declaration(&'a Declaration), Rule(&'a Rule), StyleRule(&'a StyleRule), Sheet(&'a Sheet), } impl<'a> Expression<'a> { pub fn get_id(&self) -> &String { match self { Expression::Declaration(decl) => decl.get_id(), Expression::Rule(rule) => rule.get_id(), Expression::Sheet(rule) => &rule.id, Expression::StyleRule(rule) => &rule.id, } } pub fn get_range(&self) -> &Range { match self { Expression::Declaration(decl) => decl.get_range(), Expression::Rule(rule) => rule.get_range(), Expression::Sheet(rule) => &rule.range, Expression::StyleRule(rule) => &rule.range, } } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct KeyValueDeclaration { pub id: String, pub name: String, pub value: String, pub range: Range, pub raws: BasicRaws, #[serde(rename = "nameRange")] pub name_range: Range, #[serde(rename = "valueRange")] pub value_range: Range, } impl fmt::Display for KeyValueDeclaration { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "{}:{};", &self.name, &self.value)?; Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Content { pub id: String, pub raws: BasicRaws, pub range: Range, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Include { pub id: String, #[serde(rename = "mixinName")] pub mixin_name: IncludeReference, pub declarations: Vec, pub rules: Vec, pub range: Range, pub raws: BasicRaws, } impl Include { // pub fn get_object_by_id<'a>(&'a self, id: &String) -> Option> { // get_object_by_id_in_style_rules_or_declarations(&self.rules, &self.declarations, id) // } pub fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { walk_exprs(&self.rules, visitor); walk_exprs(&self.declarations, visitor); } } impl fmt::Display for Include { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "@include {}", &self.mixin_name)?; if (self.rules.len() > 0 || self.declarations.len() > 0) { writeln!(f, "{{")?; for decl in &self.declarations { writeln!(f, "{}", decl.to_string()); } for rule in &self.rules { writeln!(f, "{}", rule.to_string()); } writeln!(f, "}}")?; } else { writeln!(f, ";"); } Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct IncludeReference { pub id: String, pub parts: Vec, pub range: Range, } impl fmt::Display for IncludeReference { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "{}", self .parts .iter() .map(|part| part.name.to_string()) .collect::>() .join(".") ); Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct IncludeReferencePart { pub id: String, pub name: String, pub range: Range, } impl fmt::Display for IncludeReferencePart { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "{}", &self.name)?; Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct CharsetRule { pub id: String, pub raws: BasicRaws, pub value: String, pub range: Range, } impl fmt::Display for CharsetRule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "charset \"{}\";", &self.value)?; Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "ruleKind")] pub enum Rule { Style(StyleRule), Comment(Comment), Charset(CharsetRule), // Namespace(String), FontFace(FontFaceRule), Media(ConditionRule), Export(ExportRule), Mixin(MixinRule), Include(Include), Supports(ConditionRule), Page(ConditionRule), Document(ConditionRule), Keyframes(KeyframesRule), } impl Rule { pub fn get_range(&self) -> &Range { match self { Rule::Comment(rule) => &rule.range, Rule::Style(rule) => &rule.range, Rule::Charset(value) => &value.range, Rule::Export(export) => &export.range, Rule::FontFace(rule) => &rule.range, Rule::Media(rule) => &rule.range, Rule::Mixin(rule) => &rule.range, Rule::Include(rule) => &rule.range, Rule::Supports(value) => &value.range, Rule::Keyframes(rule) => &rule.range, Rule::Document(rule) => &rule.range, Rule::Page(rule) => &rule.range, } } } impl Expr for Rule { fn walk<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { visitor.visit_css_rule(self); if !visitor.should_continue() { return; } match self { Rule::Style(rule) => rule.walk_inside(visitor), Rule::Export(rule) => rule.walk_inside(visitor), Rule::Media(rule) => rule.walk_inside(visitor), Rule::Mixin(rule) => rule.walk_inside(visitor), Rule::Include(rule) => rule.walk_inside(visitor), _ => {} } } fn get_id(&self) -> &String { match self { Rule::Comment(rule) => &rule.id, Rule::Style(rule) => &rule.id, Rule::Charset(value) => &value.id, Rule::Export(export) => &export.id, Rule::FontFace(rule) => &rule.id, Rule::Media(rule) => &rule.id, Rule::Mixin(rule) => &rule.id, Rule::Include(rule) => &rule.id, // Rule::Namespace(value) => &value, Rule::Supports(value) => &value.id, Rule::Keyframes(rule) => &rule.id, Rule::Document(rule) => &rule.id, Rule::Page(rule) => &rule.id, } } fn wrap<'a>(&'a self) -> pc_ast::Expression<'a> { pc_ast::Expression::CSS(Expression::Rule(self)) } } impl fmt::Display for Rule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Rule::Comment(rule) => write!(f, "{}", rule.to_string()), Rule::Style(rule) => write!(f, "{}", rule.to_string()), Rule::Charset(value) => write!(f, "@charset {}", value), Rule::Export(export) => write!(f, "{}", export), // Rule::Namespace(value) => write!(f, "@namespace {}", value), Rule::FontFace(rule) => write!(f, "{}", rule.to_string()), Rule::Media(rule) => write!(f, "{}", rule.to_string()), Rule::Mixin(rule) => write!(f, "{}", rule.to_string()), Rule::Include(rule) => write!(f, "{}", rule.to_string()), Rule::Supports(rule) => write!(f, "{}", rule.to_string()), Rule::Keyframes(rule) => write!(f, "{}", rule.to_string()), Rule::Document(rule) => write!(f, "{}", rule.to_string()), Rule::Page(rule) => write!(f, "{}", rule.to_string()), } } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Comment { pub id: String, pub value: String, pub range: Range, } impl fmt::Display for Comment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "/*{}*/", self.value) } } impl BasicRaws { pub fn new(before: Option<&[u8]>, after: Option<&[u8]>) -> BasicRaws { BasicRaws { before: if let Some(v) = before { std::str::from_utf8(v).unwrap().to_string() } else { "".to_string() }, after: if let Some(v) = after { std::str::from_utf8(v).unwrap().to_string() } else { "".to_string() }, } } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct StyleRule { pub id: String, pub selector: Selector, pub declarations: Vec, pub children: Vec, pub range: Range, pub raws: BasicRaws, } impl StyleRule { pub fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { walk_exprs(&self.children, visitor); walk_exprs(&self.declarations, visitor); } } impl fmt::Display for StyleRule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "{} {{", &self.selector)?; for decl in &self.declarations { write!(f, " {}", &decl.to_string())?; } writeln!(f, "}}")?; Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ChildRuleSelector { pub id: String, pub connector: String, pub selector: Option, pub range: Range, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ChildStyleRule { pub id: String, pub selectors: Vec, pub declarations: Vec, pub children: Vec, pub range: Range, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct FontFaceRule { pub id: String, pub declarations: Vec, pub raws: BasicRaws, pub range: Range, } impl fmt::Display for FontFaceRule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "@font-family {{")?; for decl in &self.declarations { write!(f, " {}", &decl.to_string())?; } writeln!(f, "}}")?; Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ExportRule { pub id: String, pub rules: Vec, pub range: Range, pub raws: BasicRaws, } impl fmt::Display for ExportRule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "@export {{")?; for rule in &self.rules { rule.fmt(f); } writeln!(f, "}}")?; Ok(()) } } impl ExportRule { pub fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { walk_exprs(&self.rules, visitor); } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ConditionRule { pub id: String, pub name: String, #[serde(rename = "conditionText")] pub condition_text: String, pub rules: Vec, pub declarations: Vec, pub range: Range, pub raws: BasicRaws, } impl ConditionRule { // pub fn get_object_by_id<'a>(&'a self, id: &String) -> Option> { // get_object_by_id_in_style_rules_or_declarations(&self.rules, &self.declarations, id) // } pub fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { walk_exprs(&self.rules, visitor); walk_exprs(&self.declarations, visitor); } } impl fmt::Display for ConditionRule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "@{} {} {{", &self.name, &self.condition_text)?; for rule in &self.rules { write!(f, "{}\n", &rule.to_string())?; } writeln!(f, "}}")?; Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct MixinRule { pub id: String, pub name: core_ast::StringLiteral, pub raws: BasicRaws, pub range: Range, pub declarations: Vec, pub rules: Vec, } impl fmt::Display for MixinRule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "@mixin {} {{", &self.name.value)?; for decl in &self.declarations { write!(f, "{}\n", &decl.to_string())?; } for rule in &self.rules { write!(f, "{}\n", &rule.to_string())?; } writeln!(f, "}}")?; Ok(()) } } impl MixinRule { pub fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { walk_exprs(&self.rules, visitor); walk_exprs(&self.declarations, visitor); } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct KeyframesRule { pub id: String, pub name: String, pub rules: Vec, pub range: Range, pub raws: BasicRaws, } impl fmt::Display for KeyframesRule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "@keyframes {} {{", &self.name)?; for rule in &self.rules { write!(f, "{}\n", &rule.to_string())?; } writeln!(f, "}}")?; Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct KeyframeRule { pub id: String, pub key: String, pub raws: BasicRaws, pub declarations: Vec, pub range: Range, } impl fmt::Display for KeyframeRule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "{} {{", &self.key)?; for decl in &self.declarations { write!(f, " {}", &decl.to_string())?; } writeln!(f, "}}")?; Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "selectorKind")] pub enum Selector { Group(GroupSelector), Prefixed(PrefixedSelector), Combo(ComboSelector), Descendent(DescendentSelector), PseudoElement(PseudoElementSelector), PseudoParamElement(PseudoParamElementSelector), SubElement(SubElementSelector), Not(NotSelector), Global(GlobalSelector), This(SelfSelector), Within(WithinSelector), Child(ChildSelector), Adjacent(AdjacentSelector), Sibling(SiblingSelector), Id(IdSelector), Element(ElementSelector), Attribute(AttributeSelector), Class(ClassSelector), AllSelector(AllSelector), } impl Selector { pub fn last(&self) -> &Selector { let mut curr: &Selector = self; loop { match curr { Selector::Group(selector) => { curr = selector.selectors.last().unwrap(); } Selector::Combo(selector) => { curr = selector.selectors.last().unwrap(); } Selector::Prefixed(selector) => { if let Some(postfix) = &selector.postfix_selector { curr = postfix.as_ref(); } else { return curr; } } Selector::Element(selector) => { return curr; } Selector::Descendent(selector) => { curr = selector.descendent.as_ref(); } Selector::Not(selector) => { return curr; } Selector::SubElement(selector) => { return curr; } Selector::Within(selector) => { return curr; } Selector::Global(selector) => { return curr; } Selector::This(selector) => { return curr; } Selector::Adjacent(selector) => { curr = selector.next_sibling_selector.as_ref(); } Selector::PseudoElement(selector) => { return curr; } Selector::PseudoParamElement(selector) => { return curr; } Selector::Sibling(selector) => { curr = selector.sibling_selector.as_ref(); } Selector::Child(selector) => { curr = selector.child.as_ref(); } Selector::Class(selector) => { return curr; } Selector::Id(selector) => { return curr; } Selector::Attribute(selector) => { return curr; } Selector::AllSelector(selector) => { return curr; } } } return curr; } pub fn walk(&self, each: &mut FnMut(&Selector) -> bool) -> bool { if each(self) == false { return false; } match self { Selector::PseudoElement(_) | Selector::Attribute(_) | Selector::Element(_) | Selector::AllSelector(_) | Selector::PseudoParamElement(_) | Selector::Id(_) | Selector::Class(_) => { return true; } Selector::Child(sel) => { if !each(&sel.parent) || !each(&sel.child) { return false; } } Selector::Sibling(sel) => { if !each(&sel.selector) || !each(&sel.sibling_selector) { return false; } } Selector::Group(selector) => { for child in &selector.selectors { if !child.walk(each) { return false; } } } Selector::Combo(selector) => { for part in &selector.selectors { if !part.walk(each) { return false; } } } Selector::Prefixed(selector) => { if let Some(sel) = &selector.postfix_selector { if !sel.walk(each) { return false; } } } Selector::Descendent(selector) => { if !each(&selector.ancestor) || !each(&selector.descendent) { return false; } } Selector::Not(selector) => { if !each(&selector.selector) { return false; } } Selector::SubElement(selector) => { if !each(&selector.selector) { return false; } } Selector::Within(selector) => { if !each(&selector.selector) { return false; } } Selector::Global(selector) => { if !each(&selector.selector) { return false; } } Selector::This(selector) => { if let Some(sel) = &selector.selector { if !sel.walk(each) { return false; } } } Selector::Adjacent(selector) => { if !each(&selector.selector) || !each(&selector.next_sibling_selector) { return false; } } } return true; } pub fn is_global(&self) -> bool { let mut is_global = false; self.walk(&mut |descendent| { if let Selector::Global(_) = descendent { is_global = true; return false; } return true; }); return is_global; } pub fn get_range(&self) -> &Range { match self { Selector::Group(selector) => &selector.range, Selector::Combo(selector) => &selector.range, Selector::Prefixed(selector) => &selector.range, Selector::Element(selector) => &selector.range, Selector::Descendent(selector) => &selector.range, Selector::Not(selector) => &selector.range, Selector::SubElement(selector) => &selector.range, Selector::Within(selector) => &selector.range, Selector::Global(selector) => &selector.range, Selector::This(selector) => &selector.range, Selector::Adjacent(selector) => &selector.range, Selector::PseudoElement(selector) => &selector.range, Selector::PseudoParamElement(selector) => &selector.range, Selector::Sibling(selector) => &selector.range, Selector::Child(selector) => &selector.range, Selector::Class(selector) => &selector.range, Selector::Id(selector) => &selector.range, Selector::Attribute(selector) => &selector.range, Selector::AllSelector(selector) => &selector.range, } } pub fn get_pseudo_element_name(&self) -> Option { let mut pseudo_element_name = None; self.walk(&mut |descendent| { if let Selector::PseudoElement(pseudo_element) = descendent { if matches!(pseudo_element.name.as_str(), "before" | "after") { pseudo_element_name = Some(pseudo_element.name.to_string()); return false; } } return true; }); pseudo_element_name } } impl fmt::Display for Selector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Selector::Group(selector) => write!(f, "{}", selector.to_string()), Selector::Combo(selector) => write!(f, "{}", selector.to_string()), Selector::Prefixed(selector) => write!(f, "{}", selector.to_string()), Selector::Element(selector) => write!(f, "{}", selector.to_string()), Selector::Descendent(selector) => write!(f, "{}", selector.to_string()), Selector::Not(selector) => write!(f, "{}", selector.to_string()), Selector::SubElement(selector) => write!(f, "{}", selector.to_string()), Selector::Within(selector) => write!(f, "{}", selector.to_string()), Selector::Global(selector) => write!(f, "{}", selector.to_string()), Selector::This(selector) => write!(f, "{}", selector.to_string()), Selector::Adjacent(selector) => write!(f, "{}", selector.to_string()), Selector::PseudoElement(selector) => write!(f, "{}", selector.to_string()), Selector::PseudoParamElement(selector) => write!(f, "{}", selector.to_string()), Selector::Sibling(selector) => write!(f, "{}", selector.to_string()), Selector::Child(selector) => write!(f, "{}", selector.to_string()), Selector::Class(selector) => write!(f, "{}", selector.to_string()), Selector::Id(selector) => write!(f, "{}", selector.to_string()), Selector::Attribute(selector) => write!(f, "{}", selector.to_string()), Selector::AllSelector(_) => write!(f, "*"), } } } // &--test, & + &, &:hover { } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct PrefixedSelector { pub id: String, pub connector: String, #[serde(rename = "postfixSelector")] pub postfix_selector: Option>, pub range: Range, } impl fmt::Display for PrefixedSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(postfix_selector) = &self.postfix_selector { write!(f, "&{}{}", self.connector, postfix_selector.to_string()) } else { write!(f, "&{}", self.connector) } // write!(f, "&{}", self.connector) } } // a, b, h1, h2 { } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct GroupSelector { pub id: String, pub selectors: Vec, pub range: Range, } impl fmt::Display for GroupSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let text: Vec = (&self.selectors) .into_iter() .map(|selector| selector.to_string()) .collect(); write!(f, "{}", text.join(", ")) } } // a.b[c=d] {} #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ComboSelector { pub id: String, pub selectors: Vec, pub range: Range, } impl fmt::Display for ComboSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let text: Vec = (&self.selectors) .into_iter() .map(|selector| selector.to_string()) .collect(); write!(f, "{}", text.join("")) } } // a b {} #[derive(Debug, PartialEq, Serialize, Clone)] pub struct DescendentSelector { pub id: String, pub ancestor: Box, pub descendent: Box, pub range: Range, } impl fmt::Display for DescendentSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{} {}", self.ancestor.to_string(), self.descendent.to_string() ) } } // :global(.selector) #[derive(Debug, PartialEq, Serialize, Clone)] pub struct GlobalSelector { pub id: String, pub selector: Box, pub range: Range, } impl fmt::Display for GlobalSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, ":global({})", self.selector.to_string()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct SelfSelector { pub id: String, pub selector: Option>, pub range: Range, } impl fmt::Display for SelfSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(selector) = &self.selector { write!(f, ":self({})", selector.to_string()) } else { write!(f, ":self") } } } // a > b {} #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ChildSelector { pub id: String, pub parent: Box, pub child: Box, pub range: Range, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct WithinSelector { pub id: String, pub selector: Box, pub range: Range, } impl fmt::Display for WithinSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, ":within({})", self.selector.to_string()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct NotSelector { pub id: String, pub selector: Box, pub range: Range, } impl fmt::Display for NotSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, ":not({})", self.selector.to_string()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct SubElementSelector { pub id: String, pub name: String, pub selector: Box, pub range: Range, } impl fmt::Display for SubElementSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, ":{}({})", self.name.to_string(), self.selector.to_string() ) } } impl fmt::Display for ChildSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{} > {}", self.parent.to_string(), self.child.to_string() ) } } // a + b {} #[derive(Debug, PartialEq, Serialize, Clone)] pub struct AdjacentSelector { pub selector: Box, #[serde(rename = "nextSiblingSelector")] pub next_sibling_selector: Box, pub range: Range, } impl fmt::Display for AdjacentSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{} + {}", self.selector.to_string(), self.next_sibling_selector.to_string() ) } } // a ~ b {} #[derive(Debug, PartialEq, Serialize, Clone)] pub struct SiblingSelector { pub selector: Box, #[serde(rename = "siblingSelector")] pub sibling_selector: Box, pub range: Range, } impl fmt::Display for SiblingSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{} ~ {}", self.selector.to_string(), self.sibling_selector.to_string() ) } } // div:before, div::after { } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct PseudoElementSelector { pub separator: String, // : or :: pub name: String, pub range: Range, } fn stringify_optional_selector(selector: &Option>) -> String { if let Some(item) = selector { item.to_string() } else { "".to_string() } } impl fmt::Display for PseudoElementSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}{}", &self.separator, &self.name)?; Ok(()) } } // :nth-of-type(div) { } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct PseudoParamElementSelector { pub name: String, pub param: String, pub range: Range, } impl fmt::Display for PseudoParamElementSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, ":{}({})", &self.name, &self.param)?; Ok(()) } } // div { } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ElementSelector { #[serde(rename = "tagName")] pub tag_name: String, pub range: Range, } impl fmt::Display for ElementSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", &self.tag_name)?; Ok(()) } } // .div { } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ClassSelector { #[serde(rename = "className")] pub class_name: String, pub range: Range, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct AllSelector { pub range: Range, } impl fmt::Display for AllSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "*") } } impl fmt::Display for ClassSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, ".{}", &self.class_name)?; Ok(()) } } // #div { } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct IdSelector { pub id: String, pub range: Range, } impl fmt::Display for IdSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "#{}", &self.id)?; Ok(()) } } // #div { } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct AttributeSelector { pub name: String, pub operator: Option, pub value: Option, pub range: Range, } impl fmt::Display for AttributeSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[{}", &self.name)?; if let Some(value) = &self.value { if let Some(operator) = &self.operator { write!(f, "{}{}", operator, value)?; } } write!(f, "]")?; Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Sheet { pub id: String, pub raws: BasicRaws, pub rules: Vec, pub declarations: Vec, pub range: Range, } impl Expr for Sheet { fn walk<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { visitor.visit_css_sheet(self); // walk_rules_and_decls(&self.rules, &self.declarations, visitor); walk_exprs(&self.rules, visitor); walk_exprs(&self.declarations, visitor); } fn get_id<'a>(&'a self) -> &'a String { &self.id } fn wrap<'a>(&'a self) -> pc_ast::Expression<'a> { pc_ast::Expression::CSS(Expression::Sheet(self)) } } impl fmt::Display for Sheet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for rule in &self.rules { write!(f, "{}", &rule.to_string())?; } Ok(()) } } fn walk_exprs<'a, TExpr: Expr>(exprs: &'a Vec, visitor: &mut ExprVisitor<'a>) { if !visitor.should_continue() { return; } for expr in exprs { expr.walk(visitor); if !visitor.should_continue() { return; } } } ================================================ FILE: packages/paperclip-core/native/src/css/base.rs ================================================ use serde::Serialize; use std::fmt; #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ConditionRule { pub name: String, #[serde(rename = "conditionText")] pub condition_text: String, pub rules: Vec, } impl fmt::Display for ConditionRule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "@{} {} {{", &self.name, &self.condition_text)?; for rule in &self.rules { write!(f, "{}\n", &rule.to_string())?; } writeln!(f, "}}")?; Ok(()) } } ================================================ FILE: packages/paperclip-core/native/src/css/declaration_value_ast.rs ================================================ use serde::Serialize; #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Root { pub important: bool, pub value: Expression, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "expressionKind")] pub enum Expression { List(List), Group(Group), Value(Value), } // red, blue #[derive(Debug, PartialEq, Serialize, Clone)] pub struct List { pub items: Vec, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "listItemKind")] pub enum ListItem { Group(Group), Value(Value), } // 0px -10px repeat #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Group { pub parameters: Vec, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "valueKind")] pub enum Value { Dimension(Dimension), Operation(Operation), Number(Number), String(Str), Raw(Raw), Hex(Hex), Keyword(Keyword), FunctionCall(FunctionCall), } // 10px, 100em #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Dimension { pub value: Number, pub unit: String, // px, em } // a + b #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Operation { pub left: Box, pub right: Box, pub operation: String, } // #F60 #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Hex { pub value: String, } // 'aab' #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Str { pub value: String, } // 'aab' #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Raw { pub value: String, } // 100 #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Number { pub value: String, } impl Number { pub fn to_f64(&self) -> Result { self.value.as_str().parse::() } pub fn to_f32(&self) -> Result { self.value.as_str().parse::() } pub fn to_u32(&self) -> Result { self.value.as_str().parse::() } } // repeat, no-repeat, red #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Keyword { pub value: String, } // rgba(0, 0, 100, 150px) #[derive(Debug, PartialEq, Serialize, Clone)] pub struct FunctionCall { pub name: String, pub parameters: List, } ================================================ FILE: packages/paperclip-core/native/src/css/declaration_value_parser.rs ================================================ // https://www.w3schools.com/cssref/css_selectors.asp // https://www.w3schools.com/cssref/css3_pr_mediaquery.asp // https://www.w3schools.com/cssref/css3_pr_animation-keyframes.asp // https://www.w3schools.com/cssref/pr_charset_rule.asp use super::declaration_value_ast::*; use super::tokenizer::{Token, Tokenizer}; use crate::base::parser::{get_buffer, ParseError}; use crate::base::string_scanner::StringScanner; use crate::core::id_generator::IDGenerator; type FUntil<'a> = for<'r> fn(&mut Tokenizer<'a>) -> Result; pub struct Context<'a, 'b> { tokenizer: &'a mut Tokenizer<'b>, id_generator: IDGenerator, until: FUntil<'b>, } impl<'a, 'b> Context<'a, 'b> { pub fn ended(&mut self) -> Result { Ok(self.tokenizer.scanner.is_eof() || (self.until)(&mut self.tokenizer)?) } } // screen and pub fn parse<'a>(source: &'a str, id_seed: &'a str) -> Result { let scanner = StringScanner::new(source); let mut tokenizer = Tokenizer::new_from_scanner(scanner); parse_with_tokenizer(&mut tokenizer, id_seed, |_token| Ok(false)) } pub fn parse_with_tokenizer<'a, 'b>( tokenizer: &'a mut Tokenizer<'b>, id_seed: &'a str, until: FUntil<'b>, ) -> Result { let mut context = Context { tokenizer, until, id_generator: IDGenerator::new(id_seed.to_string()), }; parse_root(&mut context) } fn parse_root<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let value = parse_expression(context)?; context.tokenizer.scanner.eat_whitespace(); let mut important = false; if !context.ended()? { if context.tokenizer.peek(1)? == Token::Bang && context.tokenizer.peek(2)? == Token::Keyword("important") { important = true; } else { return Err(ParseError::unexpected_token( context .tokenizer .scanner .get_u16pos() .range_from(context.tokenizer.scanner.get_u16pos()), )); } } Ok(Root { value, important }) } // red, blue fn parse_expression<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let mut list = parse_list(context)?; if list.items.len() == 1 { match list.items.pop().unwrap() { ListItem::Group(group) => Ok(Expression::Group(group)), ListItem::Value(value) => Ok(Expression::Value(value)), } } else { Ok(Expression::List(list)) } } // red, blue fn parse_list<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let mut items: Vec = vec![]; loop { items.push(parse_list_item(context)?); context.tokenizer.scanner.eat_whitespace(); if context.ended()? || matches!(context.tokenizer.peek(1)?, Token::Bang | Token::ParenClose) { break; } if context.tokenizer.peek(1) == Ok(Token::Comma) { context.tokenizer.next()?; } } Ok(List { items }) } fn parse_list_item<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let mut parameters: Vec = vec![]; context.tokenizer.scanner.eat_whitespace(); while !context.ended()? && !matches!( context.tokenizer.peek(1)?, Token::Bang | Token::Comma | Token::ParenClose ) { parameters.push(parse_add(context)?); } if parameters.len() == 1 { Ok(ListItem::Value(parameters.pop().unwrap())) } else { Ok(ListItem::Group(Group { parameters })) } } fn parse_add<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let left = parse_mult(context)?; if !context.ended()? && matches!(context.tokenizer.peek(1)?, Token::Plus | Token::Minus) { return Ok(Value::Operation(Operation { left: Box::new(left), operation: if context.tokenizer.next()? == Token::Plus { "+".to_string() } else { "-".to_string() }, right: Box::new(parse_add(context)?), })); } else { return Ok(left); } } fn parse_mult<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { context.tokenizer.scanner.eat_whitespace(); let left = parse_value(context)?; context.tokenizer.scanner.eat_whitespace(); if !context.ended()? && matches!(context.tokenizer.peek(1)?, Token::Star | Token::Byte(b'/')) { return Ok(Value::Operation(Operation { left: Box::new(left), operation: if context.tokenizer.next()? == Token::Star { "*".to_string() } else { "/".to_string() }, right: Box::new(parse_mult(context)?), })); } else { return Ok(left); } } fn parse_value<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let pos = context.tokenizer.scanner.get_u16pos(); let pos2 = context.tokenizer.scanner.get_pos(); match context.tokenizer.next()? { // 10px, 10%, 10em Token::Number(value) => { let number = Number { value: value.to_string(), }; context.tokenizer.scanner.eat_whitespace(); if !context.ended()? { let next = context.tokenizer.peek(1)?; let unit_option = match next { Token::Keyword(keyword) => Some(keyword.to_string()), Token::Byte(b'%') => Some("%".to_string()), _ => None, }; if let Some(unit) = unit_option { context.tokenizer.next()?; return Ok(Value::Dimension(Dimension { value: number, unit, })); } } return Ok(Value::Number(number)); } Token::Keyword(value) => { if !context.ended()? { let next = context.tokenizer.peek(1)?; if next == Token::Whitespace { return Ok(Value::Keyword(Keyword { value: value.to_string(), })); } else if next == Token::ParenOpen { context.tokenizer.next()?; // eat ( let parameters = parse_list(context)?; context.tokenizer.next_expect(Token::ParenClose)?; return Ok(Value::FunctionCall(FunctionCall { name: value.to_string(), parameters, })); } } } _ => {} } context.tokenizer.scanner.set_pos(&pos2); let buffer = get_buffer(context.tokenizer, |tokenizer| { Ok(!matches!(tokenizer.peek(1)?, Token::Whitespace | Token::Bang | Token::Comma | Token::ParenClose)) })?; return Ok(Value::Raw(Raw { value: buffer.to_string() })) } #[cfg(test)] mod tests { use super::*; #[test] fn can_smoke_parse_various_declaration_values() { let cases = [ "10px", "100rem", "10%", "red", "red !important", "rgba(0,0,0) !important", "calc(5rem + 6px * 5em / 5 - 3em)", "inset 0 1px 2px rgba(0, 0, 0, 0.15)", "15px calc(15px / 3) 4px 2px", "url('https://example.com/images/myImg.jpg')", "url(\"./arrowSmall-down.svg\")", "image(url(mask.png), skyblue, linear-gradient(rgba(0, 0, 0, 1.0), transparent))" ]; for case in cases.iter() { let expr = parse(case, "").unwrap(); // println!("{:?}", expr); } // panic!("blah"); } } ================================================ FILE: packages/paperclip-core/native/src/css/media_ast.rs ================================================ // https://developer.mozilla.org/en-US/docs/Web/CSS/@media#media-query-list use super::declaration_value_ast as decl_ast; use serde::Serialize; #[derive(Debug, PartialEq, Serialize, Clone)] pub struct MediaQueryList { pub queries: Vec, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "mediaQueryKind")] pub enum MediaQuery { // Condition(MediaCondition), Ident(MediaIdent), } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct MediaIdent { pub only: Option, // not | only, pub media_type: String, // screen, print pub and_condition: Option, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "mediaConditionWithoutOrKind")] pub enum MediaConditionWithoutOr { MediaNot(MediaNot), MediaAnd(MediaCompound), InParens(MediaInParens), } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct MediaNot { pub condition: Box, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct MediaCompound { pub condition: Box, pub rest: Vec, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "mediaInParensKind")] pub enum MediaInParens { Condition(MediaCondition), Feature(MediaFeature), } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "mediaFeatureKind")] pub enum MediaFeature { Plain(MFPlain), // Boolean(MFBoolean), // Range(MFRange) } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "mediaFeatureKind")] pub struct MFPlain { pub name: String, pub value: decl_ast::Expression, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "mediaConditionKind")] pub enum MediaCondition { MediaNot(MediaNot), MediaAnd(MediaCompound), MediaOr(MediaCompound), } ================================================ FILE: packages/paperclip-core/native/src/css/media_parser.rs ================================================ // https://www.w3schools.com/cssref/css_selectors.asp // https://www.w3schools.com/cssref/css3_pr_mediaquery.asp // https://www.w3schools.com/cssref/css3_pr_animation-keyframes.asp // https://www.w3schools.com/cssref/pr_charset_rule.asp use super::declaration_value_parser::parse_with_tokenizer as parse_decl_value_with_tokenizer; use super::media_ast::*; use super::tokenizer::{Token, Tokenizer}; use crate::base::parser::ParseError; use crate::base::string_scanner::StringScanner; use crate::core::id_generator::IDGenerator; type FUntil<'a> = for<'r> fn(&mut Tokenizer<'a>) -> Result; pub struct Context<'a> { tokenizer: Tokenizer<'a>, id_generator: IDGenerator, until: FUntil<'a>, } impl<'a> Context<'a> { pub fn ended(&mut self) -> Result { Ok(self.tokenizer.scanner.is_eof() || (self.until)(&mut self.tokenizer)?) } } // screen and pub fn parse<'a>(source: &'a str, id_seed: &'a str) -> Result { let mut scanner = StringScanner::new(&source); let mut tokenizer = Tokenizer::new_from_scanner(scanner); parse_with_tokenizer(tokenizer, id_seed, |_token| Ok(false)) } pub fn parse_with_tokenizer<'a>( tokenizer: Tokenizer<'a>, id_seed: &'a str, until: FUntil<'a>, ) -> Result { let mut context = Context { tokenizer, until, id_generator: IDGenerator::new(id_seed.to_string()), }; parse_media_query_list(&mut context) } // screen, print, screen and (max-width: 400px) fn parse_media_query_list<'a>(context: &mut Context<'a>) -> Result { let mut queries: Vec = vec![]; loop { queries.push(parse_media_query(context)?); if !context.ended()? { context.tokenizer.scanner.eat_whitespace(); context.tokenizer.next_expect(Token::Comma)?; } else { break; } } Ok(MediaQueryList { queries }) } fn parse_media_query<'a>(context: &mut Context<'a>) -> Result { context.tokenizer.scanner.eat_whitespace(); // only | not let only = if let Token::Keyword(keyword) = context.tokenizer.peek(1)? { if keyword == "only" { context.tokenizer.next()?; Some(true) } else if keyword == "not" { context.tokenizer.next()?; Some(false) } else { None } } else { None }; context.tokenizer.scanner.eat_whitespace(); let media_pos = context.tokenizer.scanner.get_u16pos(); // print, screen, etc let media_type = if let Token::Keyword(ident) = context.tokenizer.next()? { ident } else { return Err(ParseError::unexpected_token( media_pos.range_from(context.tokenizer.scanner.get_u16pos()), )); }; context.tokenizer.scanner.eat_whitespace(); let and_condition = if !context.ended()? { if let Token::Keyword(and) = context.tokenizer.peek(1)? { if and == "and" { context.tokenizer.next()?; // eat "and" Some(parse_media_condition_without_or(context)?) } else { None } } else { None } } else { None }; return Ok(MediaQuery::Ident(MediaIdent { only, media_type: media_type.to_string(), and_condition, })); } fn parse_media_condition_without_or<'a>( context: &mut Context<'a>, ) -> Result { context.tokenizer.scanner.eat_whitespace(); let not = if context.tokenizer.peek(1)? == Token::Keyword("not") { context.tokenizer.next()?; true } else { false }; let left = parse_media_in_parens(context)?; if not { return Ok(MediaConditionWithoutOr::MediaNot(MediaNot { condition: Box::new(left), })); } context.tokenizer.scanner.eat_whitespace(); if !context.ended()? { Ok(MediaConditionWithoutOr::MediaAnd(MediaCompound { condition: Box::new(left), rest: parse_media_condition_rest("and", context)?, })) } else { Ok(MediaConditionWithoutOr::InParens(left)) } } fn parse_media_condition_rest<'a>( keyword: &'a str, context: &mut Context<'a>, ) -> Result, ParseError> { let mut rest: Vec = vec![]; loop { context.tokenizer.scanner.eat_whitespace(); context.tokenizer.next_expect(Token::Keyword(keyword))?; context.tokenizer.scanner.eat_whitespace(); rest.push(parse_media_in_parens(context)?); if context.ended()? { break; } } Ok(rest) } fn parse_media_in_parens<'a>(context: &mut Context<'a>) -> Result { context.tokenizer.scanner.eat_whitespace(); if context.tokenizer.peek(1)? == Token::ParenOpen { context.tokenizer.next_expect(Token::ParenOpen)?; let inner = parse_media_in_parens_inner(context)?; context.tokenizer.next_expect(Token::ParenClose)?; Ok(inner) } else { parse_media_in_parens_inner(context) } } fn parse_media_in_parens_inner<'a>(context: &mut Context<'a>) -> Result { let left = parse_media_feature(context)?; Ok(MediaInParens::Feature(left)) } fn parse_media_feature<'a>(context: &mut Context<'a>) -> Result { context.tokenizer.scanner.eat_whitespace(); let pos = context.tokenizer.scanner.get_u16pos(); let name = if let Token::Keyword(keyword) = context.tokenizer.next()? { keyword.to_string() } else { return Err(ParseError::unexpected_token( pos.range_from(context.tokenizer.scanner.get_u16pos()), )); }; context.tokenizer.scanner.eat_whitespace(); context.tokenizer.next_expect(Token::Colon)?; context.tokenizer.scanner.eat_whitespace(); let value = parse_decl_value_with_tokenizer(&mut context.tokenizer, "", |tokenizer| { Ok(matches!( tokenizer.peek(1)?, Token::Whitespace | Token::ParenClose )) })?; Ok(MediaFeature::Plain(MFPlain { name, value: value.value, })) } #[cfg(test)] mod tests { use super::*; #[test] fn can_smoke_parse_various_media_queries() { let cases = ["screen", "screen and (max-width: 100px)"]; for case in cases.iter() { parse(case, "id").unwrap(); } } } ================================================ FILE: packages/paperclip-core/native/src/css/mod.rs ================================================ pub mod ast; pub mod base; pub mod declaration_value_ast; pub mod declaration_value_parser; pub mod media_ast; pub mod media_parser; pub mod parser; pub mod runtime; pub mod tokenizer; ================================================ FILE: packages/paperclip-core/native/src/css/parser.rs ================================================ // https://www.w3schools.com/cssref/css_selectors.asp // https://www.w3schools.com/cssref/css3_pr_mediaquery.asp // https://www.w3schools.com/cssref/css3_pr_animation-keyframes.asp // https://www.w3schools.com/cssref/pr_charset_rule.asp use super::ast::*; use super::tokenizer::{Token, Tokenizer}; use crate::base::ast::{BasicRaws, Range}; use crate::base::parser::{get_buffer, ParseError}; use crate::base::string_scanner::{StringScanner, U16Position}; use crate::core::ast as css_ast; use crate::core::id_generator::IDGenerator; use std::collections::HashMap; use std::sync::Mutex; type FUntil<'a> = for<'r> fn(&mut Tokenizer<'a>) -> Result; pub struct Context<'a, 'b> { tokenizer: &'a mut Tokenizer<'b>, until: FUntil<'b>, id_generator: IDGenerator, } impl<'a, 'b> Context<'a, 'b> { pub fn ended(&mut self) -> Result { Ok(self.tokenizer.scanner.is_eof() || (self.until)(&mut self.tokenizer)?) } } pub fn parse<'a, 'b>(source: &'a str, id_seed: String) -> Result { let mut scanner = StringScanner::new(source); let mut tokenizer = Tokenizer::new_from_scanner(scanner); let result = parse_with_tokenizer(&mut tokenizer, id_seed.as_str(), |_token| Ok(false)); return result; } pub fn parse_selector<'a, 'b>( selector: &'a str, id_seed: Option, ) -> Result { lazy_static! { static ref CACHE: Mutex>> = Mutex::new(HashMap::new()); } let mut cache = CACHE.lock().unwrap(); let id_seed2 = id_seed.unwrap_or("0".to_string()); let cache_key = format!("{}{}", selector, id_seed2); if let Some(result) = cache.get(&cache_key) { return result.clone(); } let rule_source = format!("{}{{}}", selector); let ret: Result = if let Ok(ast) = parse(&rule_source, id_seed2) { match ast.rules.get(0).unwrap() { Rule::Style(style) => Ok(style.selector.clone()), _ => Err(ParseError::unknown()), } } else { return Err(ParseError::unexpected( format!("Unable to parse selector \"{}\"", selector), Range::nil(), )); }; cache.insert(cache_key, ret.clone()); ret } pub fn parse_with_tokenizer<'a, 'b>( tokenizer: &mut Tokenizer<'b>, id_seed: &'a str, until: FUntil<'b>, ) -> Result { let mut context = Context { tokenizer, until, id_generator: IDGenerator::new(id_seed.to_string()), }; parse_sheet(&mut context) } fn eat_comments<'a, 'b>( context: &mut Context<'a, 'b>, start: Token, end: Token, ) -> Result<(), ParseError> { if context.ended()? || context.tokenizer.peek(1)? != start { return Ok(()); } context.tokenizer.next()?; // eat
    {child}
    } /> " .to_string(), )] .iter() .cloned() .collect(); assert_get_source_info( &graph, vec![0, 0, 0], ast::ExprSource { id: "e93244ce".to_string(), text_source: Some(ast::ExprTextSource { uri: "entry.pc".to_string(), range: ast::Range::new(U16Position::new(187, 11, 22), U16Position::new(198, 11, 33)), }), }, ); } fn assert_get_source_info( graph: &HashMap, path: Vec, expected: ast::ExprSource, ) { let mut mock_engine = create_mock_engine(&graph); block_on(mock_engine.run(&"entry.pc".to_string())); assert_eq!( mock_engine.get_virtual_node_source_info(&NodeSource { path, document_uri: "entry.pc".to_string() }), Some(expected) ); } } pub fn __test__evaluate_pc_files<'a>( files: BTreeMap, main_file_name: &'a str, ) -> (BTreeMap, DependencyGraph) { let f1 = files.clone(); let f2 = files.clone(); let mut engine = Engine::new( Box::new(move |uri| f1.get(uri).unwrap().clone()), Box::new(move |uri| f2.get(uri) != None), Box::new(|_, uri| Some(uri.to_string())), None, false, EngineMode::SingleFrame, ); block_on(engine.run(&main_file_name.to_string())); ( engine.evaluated_data.clone(), engine.dependency_graph.clone(), ) } ================================================ FILE: packages/paperclip-core/native/src/engine/errors.rs ================================================ use crate::base::parser::ParseError; use crate::base::runtime::RuntimeError; use crate::core::graph::{Dependency, DependencyContent, DependencyGraph, GraphError}; use serde::Serialize; #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "errorKind")] pub enum EngineError { Graph(GraphError), Runtime(RuntimeError), } ================================================ FILE: packages/paperclip-core/native/src/engine/mod.rs ================================================ pub mod diagnostics; pub mod engine; pub mod errors; pub mod test_utils; ================================================ FILE: packages/paperclip-core/native/src/engine/test_utils.rs ================================================ use crate::engine::engine::Engine; use crate::pc::runtime::evaluator::EngineMode; use std::collections::HashMap; pub fn create_mock_engine<'a>(graph: &HashMap) -> Engine { let graph2 = graph.clone(); let graph3 = graph.clone(); Engine::new( Box::new(move |uri| { return graph2[uri].to_string(); }), Box::new(move |uri| graph3.get(uri) != None), Box::new(|a, b| Some(b.to_string())), None, false, EngineMode::SingleFrame, ) } ================================================ FILE: packages/paperclip-core/native/src/lib.rs ================================================ extern crate wasm_bindgen; use serde::Serialize; use wasm_bindgen::prelude::*; #[macro_use] extern crate matches; extern crate console_error_panic_hook; #[macro_use] extern crate lazy_static; mod annotation; mod base; mod core; mod coverage; mod css; mod engine; mod pc; mod script; use crate::coverage::reporter::CoverageReport; use crate::pc::runtime::evaluator::EngineMode; use crate::pc::runtime::inspect_node_styles::InspectionOptions; use crate::pc::runtime::lint::LintOptions; use crate::pc::runtime::virt as pc_virt; use ::futures::executor::block_on; use engine::engine::Engine; #[cfg(feature = "wee_alloc")] #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; extern crate web_sys; #[wasm_bindgen] extern "C" { fn alert(s: &str); } #[wasm_bindgen] pub struct NativeEngine { target: Engine, } #[wasm_bindgen] #[derive(Debug, PartialEq, Serialize)] #[serde(tag = "kind")] pub enum NativeEngineMode { SingleFrame, MultiFrame, } #[wasm_bindgen] impl NativeEngine { pub fn new( read_file: js_sys::Function, file_exists: js_sys::Function, resolve_file: js_sys::Function, get_lint_config: js_sys::Function, engine_mode: NativeEngineMode, ) -> NativeEngine { console_error_panic_hook::set_once(); NativeEngine { target: Engine::new( Box::new(move |uri| { let this = JsValue::NULL; let arg = JsValue::from(uri); read_file.call1(&this, &arg).unwrap().as_string().unwrap() }), Box::new(move |uri| { let this = JsValue::NULL; let arg = JsValue::from(uri); file_exists.call1(&this, &arg).unwrap().as_bool().unwrap() }), Box::new(move |from_path, relative_path| { let this = JsValue::NULL; let arg = JsValue::from(from_path); let arg2 = JsValue::from(relative_path); resolve_file.call2(&this, &arg, &arg2).unwrap().as_string() }), Some(Box::new(move |uri| { let this = JsValue::NULL; let arg = JsValue::from(uri); get_lint_config .call1(&this, &arg) .unwrap() .into_serde() .unwrap_or(None) })), false, match engine_mode { NativeEngineMode::SingleFrame => EngineMode::SingleFrame, NativeEngineMode::MultiFrame => EngineMode::MultiFrame, }, ), } } pub fn generate_coverage_report(&mut self) -> JsValue { let result = block_on(self.target.generate_coverage_report()); JsValue::from_serde(&result).unwrap() } pub fn load(&mut self, uri: String) -> JsValue { let result = block_on(self.target.load(&uri)); JsValue::from_serde(&result).unwrap() } pub fn run(&mut self, uri: String) -> JsValue { let result = block_on(self.target.run(&uri)); JsValue::from_serde(&result).unwrap() } pub fn get_graph_uris(&self) -> JsValue { let result = self.target.get_graph_uris(); JsValue::from_serde(&result).unwrap() } pub fn reset(&mut self) { self.target.reset() } pub fn add_listener(&mut self, listener: js_sys::Function) { self.target.add_listener(Box::new(move |event| { let this = JsValue::NULL; let arg = JsValue::from_serde(&event).unwrap(); listener.call1(&this, &arg).unwrap(); })); } pub fn lint_file(&mut self, uri: String) -> JsValue { JsValue::from_serde(&self.target.lint_file(&uri)).unwrap() } pub fn get_virtual_node_source_info(&mut self, path: Vec, uri: String) -> JsValue { JsValue::from_serde( &self .target .get_virtual_node_source_info(&pc_virt::NodeSource { path, document_uri: uri, }), ) .unwrap() } pub fn get_loaded_ast(&mut self, uri: String) -> JsValue { let result = self.target.get_loaded_ast(&uri); JsValue::from_serde(&result).unwrap() } pub fn get_dependency(&mut self, uri: String) -> JsValue { let result = self.target.get_dependency(&uri); JsValue::from_serde(&result).unwrap() } pub fn parse_content(&mut self, content: String, uri: String) -> JsValue { let result = block_on(self.target.parse_content(&content, &uri)); JsValue::from_serde(&result).unwrap() } pub fn parse_file(&mut self, uri: String) -> JsValue { let result = block_on(self.target.parse_file(&uri)); JsValue::from_serde(&result).unwrap() } pub fn purge_unlinked_files(&mut self) { block_on(self.target.purge_unlinked_files()); } pub fn get_expression_by_id(&self, id: String) -> JsValue { JsValue::from_serde(&self.target.get_expression_by_id(&id)).unwrap() } pub fn inspect_node_styles( &mut self, path: Vec, uri: String, screen_width: u32, ) -> JsValue { let result = self.target.inspect_node_styles( &pc_virt::NodeSource { path, document_uri: uri, }, &InspectionOptions { screen_width: Some(screen_width), include_inherited: true, }, ); JsValue::from_serde(&result).unwrap() } pub fn update_virtual_file_content(&mut self, uri: String, content: String) { console_error_panic_hook::set_once(); block_on(self.target.update_virtual_file_content(&uri, &content)); } } ================================================ FILE: packages/paperclip-core/native/src/pc/ast.rs ================================================ use crate::annotation::ast as annotation_ast; use crate::base::ast::{BasicRaws, Range}; use crate::core::ast as core_ast; use crate::core::ast::{walk_exprs, Expr, ExprVisitor}; use crate::css::ast as css_ast; use crate::script::ast as script_ast; use serde::Serialize; use std::fmt; use std::str; #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ElementRaws { pub before: String, } impl ElementRaws { pub fn new(before: &[u8]) -> ElementRaws { ElementRaws { before: str::from_utf8(before).unwrap().to_string(), } } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Element { pub id: String, pub raws: ElementRaws, pub range: Range, #[serde(rename = "openTagRange")] pub open_tag_range: Range, #[serde(rename = "tagNameRange")] pub tag_name_range: Range, #[serde(rename = "tagName")] pub tag_name: String, pub attributes: Vec, pub children: Vec, } impl Element { fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { walk_exprs(&self.children, visitor); walk_exprs(&self.attributes, visitor); } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Comment { pub id: String, pub raws: BasicRaws, pub range: Range, pub annotation: annotation_ast::Annotation, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ValueObject { pub id: String, pub range: Range, pub value: String, pub raws: BasicRaws, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "nodeKind")] pub enum Node { Text(ValueObject), Comment(Comment), Element(Element), Fragment(Fragment), StyleElement(StyleElement), Slot(Slot), } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "pcObjectKind")] pub enum Expression<'a> { Node(&'a Node), Attribute(&'a Attribute), CSS(css_ast::Expression<'a>), Script(&'a script_ast::Expression), String(&'a core_ast::StringLiteral), } impl<'a> Expression<'a> { pub fn get_range(&'a self) -> &'a Range { match self { Expression::Node(node) => node.get_range(), Expression::Attribute(attr) => attr.get_range(), Expression::CSS(css) => css.get_range(), Expression::Script(js) => js.get_range(), Expression::String(value) => &value.range, } } } impl Node { pub fn get_range(&self) -> &Range { match self { Node::Text(value) => &value.range, Node::Comment(value) => &value.range, Node::Element(value) => &value.range, Node::Fragment(value) => &value.range, Node::StyleElement(value) => &value.range, Node::Slot(value) => &value.range, } } pub fn get_id(&self) -> &String { match self { Node::Text(value) => &value.id, Node::Comment(value) => &value.id, Node::Element(value) => &value.id, Node::Fragment(value) => &value.id, Node::StyleElement(value) => &value.id, Node::Slot(value) => &value.id, } } pub fn get_children<'a>(&'a self) -> Option<&'a Vec> { match &self { Node::Element(root) => Some(&root.children), Node::Fragment(root) => Some(&root.children), _ => None, } } pub fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { match self { Node::Slot(slot) => { slot.walk_inside(visitor); } Node::StyleElement(style) => { style.walk_inside(visitor); } Node::Fragment(fragment) => { fragment.walk_inside(visitor); } Node::Element(element) => { element.walk_inside(visitor); } Node::Comment(_) | Node::Text(_) => {} } } } impl Expr for Node { fn walk<'a>(&'a self, visitor: &mut dyn ExprVisitor<'a>) { visitor.visit_node(self); if !visitor.should_continue() { return; } self.walk_inside(visitor); } fn get_id<'a>(&'a self) -> &'a String { self.get_id() } fn wrap<'a>(&'a self) -> Expression<'a> { return Expression::Node(self); } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Slot { pub id: String, // !{slot} #[serde(rename = "omitFromCompilation")] pub omit_from_compilation: bool, pub script: script_ast::Expression, pub range: Range, pub raws: BasicRaws, } impl Slot { fn walk_inside<'a>(&'a self, visitor: &mut dyn ExprVisitor<'a>) { self.script.walk(visitor); } } impl fmt::Display for Node { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Node::Text(text) => write!(f, "{}", &text.value), Node::Slot(slot) => write!(f, "{{{}}}", &slot.script.to_string()), Node::Comment(comment) => write!(f, ""), Node::Fragment(node) => write!(f, "{}", node.to_string()), Node::Element(element) => write!(f, "{}", element.to_string()), Node::StyleElement(element) => write!(f, "{}", element.to_string()), } } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct AttributeSlotValue { pub id: String, pub script: script_ast::Expression, pub range: Range, } impl AttributeSlotValue { pub fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { self.script.walk(visitor); } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct AttributeDynamicStringValue { pub id: String, pub values: Vec, pub range: Range, } impl AttributeDynamicStringValue { pub fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { for value in &self.values { value.walk(visitor); if !visitor.should_continue() { break; } } } } impl fmt::Display for AttributeDynamicStringValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for value in &self.values { match value { AttributeDynamicStringPart::Slot(stmt) => { stmt.fmt(f); } AttributeDynamicStringPart::ClassNamePierce(stmt) => { write!(f, "${}", stmt.class_name); } AttributeDynamicStringPart::Literal(stmt) => { write!(f, "{}", stmt.value); } } } Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "partKind")] pub enum AttributeDynamicStringPart { Literal(core_ast::StringLiteral), ClassNamePierce(AttributeDynamicStringClassNamePierce), Slot(script_ast::Expression), } impl AttributeDynamicStringPart { pub fn get_id(&self) -> &String { match self { AttributeDynamicStringPart::ClassNamePierce(expr) => &expr.id, AttributeDynamicStringPart::Literal(expr) => &expr.id, AttributeDynamicStringPart::Slot(expr) => &expr.get_id(), } } pub fn walk<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { match self { AttributeDynamicStringPart::ClassNamePierce(_) | AttributeDynamicStringPart::Literal(_) => {} AttributeDynamicStringPart::Slot(slot) => { slot.walk(visitor); } } } } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "className")] pub struct AttributeDynamicStringClassNamePierce { pub id: String, #[serde(rename = "className")] pub class_name: String, pub range: Range, } pub fn fmt_attributes(attributes: &Vec, f: &mut fmt::Formatter) -> fmt::Result { for attribute in attributes { write!(f, " {}", attribute.to_string())?; } Ok(()) } pub fn fmt_start_tag<'a>( tag_name: &'a str, attributes: &Vec, f: &mut fmt::Formatter, ) -> fmt::Result { write!(f, "<{}", tag_name)?; fmt_attributes(attributes, f)?; write!(f, ">")?; Ok(()) } pub fn fmt_end_tag<'a>(tag_name: &'a str, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "", tag_name)?; Ok(()) } impl fmt::Display for Element { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt_start_tag(&self.tag_name.as_str(), &self.attributes, f)?; for child in &self.children { write!(f, "{} ", child.to_string())?; } fmt_end_tag(&self.tag_name.as_str(), f)?; Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "attrKind")] pub enum Attribute { ShorthandAttribute(ShorthandAttribute), SpreadAttribute(SpreadAttribute), KeyValueAttribute(KeyValueAttribute), PropertyBoundAttribute(PropertyBoundAttribute), } impl Attribute { pub fn get_id(&self) -> &String { match self { Attribute::ShorthandAttribute(expr) => &expr.id, Attribute::SpreadAttribute(expr) => &expr.id, Attribute::KeyValueAttribute(expr) => &expr.id, Attribute::PropertyBoundAttribute(expr) => &expr.id, } } pub fn get_range(&self) -> &Range { match self { Attribute::ShorthandAttribute(expr) => &expr.range, Attribute::SpreadAttribute(expr) => &expr.range, Attribute::KeyValueAttribute(expr) => &expr.range, Attribute::PropertyBoundAttribute(expr) => &expr.range, } } } impl Expr for Attribute { fn get_id<'a>(&'a self) -> &'a String { match self { Attribute::ShorthandAttribute(expr) => &expr.id, Attribute::SpreadAttribute(expr) => &expr.id, Attribute::KeyValueAttribute(expr) => &expr.id, Attribute::PropertyBoundAttribute(expr) => &expr.id, } } fn walk<'a>(&'a self, visitor: &mut dyn ExprVisitor<'a>) { visitor.visit_attr(self); if !visitor.should_continue() { return; } match self { Attribute::KeyValueAttribute(kv) => kv.walk_inside(visitor), Attribute::ShorthandAttribute(kv) => kv.walk_inside(visitor), Attribute::PropertyBoundAttribute(kv) => kv.walk_inside(visitor), Attribute::SpreadAttribute(kv) => kv.walk_inside(visitor), } } fn wrap<'a>(&'a self) -> Expression<'a> { return Expression::Attribute(self); } } impl fmt::Display for Attribute { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Attribute::ShorthandAttribute(attr) => attr.fmt(f), Attribute::KeyValueAttribute(attr) => attr.fmt(f), Attribute::SpreadAttribute(attr) => attr.fmt(f), Attribute::PropertyBoundAttribute(attr) => attr.fmt(f), } } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct SpreadAttribute { pub id: String, // !{...slot} #[serde(rename = "omitFromCompilation")] pub omit_from_compilation: bool, pub script: script_ast::Expression, pub range: Range, } impl SpreadAttribute { pub fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { self.script.walk(visitor); } } impl fmt::Display for SpreadAttribute { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{{...{}}}", self.script.to_string()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ShorthandAttribute { pub id: String, pub reference: script_ast::Expression, pub range: Range, } impl ShorthandAttribute { pub fn get_name(&self) -> Result<&String, &'static str> { match &self.reference { script_ast::Expression::Reference(reference) => { if reference.path.len() == 1 { Ok(&reference.path[0].name) } else { Err("Unexpected Expression") } } _ => Err("Unexpected Expression"), } } pub fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { self.reference.walk(visitor); } } impl fmt::Display for ShorthandAttribute { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{{{}}}", self.reference.to_string()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct PropertyBoundAttribute { pub id: String, #[serde(rename = "bindingName")] pub binding_name: String, pub name: String, pub range: Range, pub value: Option, } impl PropertyBoundAttribute { pub fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { if let Some(value) = &self.value { value.walk(visitor); } } } impl fmt::Display for PropertyBoundAttribute { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}", self.name, self.binding_name)?; if self.value == None { Ok(()) } else { write!(f, "={}", self.value.as_ref().unwrap().to_string()) } } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct KeyValueAttribute { pub id: String, pub name: String, pub range: Range, pub value: Option, } impl KeyValueAttribute { pub fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { if let Some(value) = &self.value { value.walk(visitor); } } } impl fmt::Display for KeyValueAttribute { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name)?; if self.value == None { Ok(()) } else { write!(f, "={}", self.value.as_ref().unwrap().to_string()) } } } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "attrValueKind")] pub enum AttributeValue { DyanmicString(AttributeDynamicStringValue), String(core_ast::StringLiteral), Slot(AttributeSlotValue), } impl AttributeValue { pub fn get_id(&self) -> &String { match self { AttributeValue::DyanmicString(value) => &value.id, AttributeValue::String(value) => &value.id, AttributeValue::Slot(value) => &value.id, } } pub fn walk<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { match self { AttributeValue::DyanmicString(ds) => ds.walk_inside(visitor), AttributeValue::Slot(ds) => ds.walk_inside(visitor), AttributeValue::String(ds) => ds.walk(visitor), } } } impl fmt::Display for AttributeValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self { AttributeValue::String(value) => write!(f, "{}", value.value), AttributeValue::Slot(value) => write!(f, "{{{}}}", value.script.to_string()), AttributeValue::DyanmicString(script) => write!(f, "{{{}}}", script.to_string()), } } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct StyleElement { pub id: String, pub raws: ElementRaws, pub attributes: Vec, pub sheet: css_ast::Sheet, pub range: Range, } impl StyleElement { fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { self.sheet.walk(visitor); } } impl fmt::Display for StyleElement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt_start_tag("style", &self.attributes, f)?; write!(f, "{}", self.sheet.to_string())?; fmt_end_tag("style", f)?; Ok(()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Fragment { pub id: String, pub range: Range, pub children: Vec, } impl Fragment { fn walk_inside<'a>(&'a self, visitor: &mut ExprVisitor<'a>) { walk_exprs(&self.children, visitor); } } impl fmt::Display for Fragment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "")?; for child in &self.children { write!(f, "{}", child.to_string())?; } Ok(()) } } pub fn get_imports<'a>(root_expr: &'a Node) -> Vec<&'a Element> { let mut imports = vec![]; let children = root_expr.get_children(); if children != None { for child in children.unwrap() { if let Node::Element(element) = &child { if element.tag_name == "import" && get_attribute_value("src", element) != None { imports.push(element); } } } } imports } pub fn get_tag_name<'a>(element: &'a Element) -> String { if element.tag_name.contains(".") { element.tag_name[..element.tag_name.find(".").unwrap()].to_string() } else { element.tag_name.to_string() } } pub fn get_tag_namespace<'a>(element: &'a Element) -> Option { if element.tag_name.contains(".") { Some(element.tag_name[(element.tag_name.find(".").unwrap() + 1)..].to_string()) } else { None } } pub fn get_parts<'a>(root_expr: &'a Node) -> Vec<&'a Element> { let mut parts = vec![]; let children = root_expr.get_children(); if children != None { for child in children.unwrap() { if let Node::Element(element) = &child { if has_attribute("component", element) { parts.push(element); } } } } parts } pub fn get_part_by_id<'a>(id: &String, root_expr: &'a Node) -> Option<&'a Element> { get_parts(root_expr) .iter() .find(|element| get_attribute_value("as", element) == Some(id)) .map(|element| *element) } pub fn get_import_by_id<'a>(id: &String, root_expr: &'a Node) -> Option<&'a Element> { for import in get_imports(root_expr).iter() { if get_attribute_value("as", import) == Some(id) { return Some(import); } } None } pub fn get_import_by_src<'a>(src: &String, root_expr: &'a Node) -> Option<&'a Element> { for import in get_imports(root_expr).iter() { if get_attribute_value("src", import) == Some(src) { return Some(import); } } None } pub fn get_attribute<'a, 'b>(name: &'b str, element: &'a Element) -> Option<&'a Attribute> { for attribute in &element.attributes { if let Attribute::KeyValueAttribute(attr) = attribute { if attr.name == name { return Some(&attribute); } } } None } pub fn get_attribute_value<'a, 'b>(name: &'b str, element: &'a Element) -> Option<&'a String> { let attr = get_attribute(name, element); if let Some(att) = attr { if let Attribute::KeyValueAttribute(kv_attribute) = att { if let Some(expr) = &kv_attribute.value { if let AttributeValue::String(st) = &expr { return Some(&st.value); } } } } None } pub fn has_attribute<'a, 'b>(name: &'b str, element: &'a Element) -> bool { match get_attribute(name, element) { Some(attr) => true, None => false, } } pub fn get_import_ids<'a>(root_expr: &'a Node) -> Vec<&'a String> { let mut ids = vec![]; for import in get_imports(root_expr) { if let Some(id) = get_attribute_value("as", &import) { ids.push(id); } } ids } pub fn get_import_identifier<'a>(import: &'a Element) -> Option<&'a String> { if let Some(id) = get_attribute_value("as", &import) { return Some(id); } if let Some(src) = get_attribute_value("src", &import) { return Some(src); } return None; } pub fn get_import_by_identifier<'a>(id: &String, root_expr: &'a Node) -> Option<&'a Element> { for import in get_imports(root_expr) { if get_attribute_value("as", &import) == Some(id) || get_attribute_value("src", &import) == Some(id) { return Some(import); } } return None; } pub fn get_part_ids<'a>(root_expr: &'a Node) -> Vec<&'a String> { let mut ids = vec![]; // DEPRECATED for part in get_parts(root_expr) { if let Some(id) = get_attribute_value("as", &part) { ids.push(id); } } ids } ================================================ FILE: packages/paperclip-core/native/src/pc/get_info.rs ================================================ use super::ast; pub struct PCASTInfo { component_paths: Vec> } pub fn get_info(node: Node) -> PCASTInfo { PCASTInfo { component_paths: collect_component_pat } } ================================================ FILE: packages/paperclip-core/native/src/pc/mod.rs ================================================ pub mod ast; pub mod parser; pub mod runtime; pub mod tokenizer; ================================================ FILE: packages/paperclip-core/native/src/pc/parser.rs ================================================ // https://tympanus.net/codrops/css_reference/ use super::ast as pc_ast; use super::tokenizer::{Token, Tokenizer}; use crate::annotation::parser::parse_with_tokenizer as parse_annotation_with_tokenizer; use crate::annotation::tokenizer::{Token as AnnotationToken, Tokenizer as AnnotationTokenizer}; use crate::base::ast::{BasicRaws, Range}; use crate::base::parser::{get_buffer, ParseError}; use crate::base::string_scanner::StringScanner; use crate::base::string_scanner::U16Position; use crate::base::utils::get_document_id; use crate::core::ast as core_ast; use crate::core::id_generator::IDGenerator; use crate::css::parser::parse_with_tokenizer as parse_css_with_tokenizer; use crate::css::tokenizer::{Token as CSSToken, Tokenizer as CSSTokenizer}; use crate::script::ast as script_ast; use crate::script::parser::parse_with_tokenizer as parse_js_with_tokenizer; use crate::script::tokenizer::Tokenizer as JSTokenizer; use crc::crc32; use std::str; /* void elements: [ 'area', 'base', 'basefont', 'bgsound', 'br', 'col', 'command', 'embed', 'frame', 'hr', 'image', 'img', 'input', 'isindex', 'keygen', 'link', 'menuitem', 'meta', 'nextid', 'param', 'source', 'track', 'wbr' ] */ pub fn parse<'a, 'b>( source: &'a str, source_uri: &'a str, id_seed: &'b str, ) -> Result { let mut scanner = StringScanner::new(source); let mut tokenizer = Tokenizer::new_from_scanner(scanner); parse_fragment( &mut Context { tokenizer: tokenizer, scope_id: get_document_id(&source_uri.to_string()), id_generator: IDGenerator::new(id_seed.to_string()), }, vec![], ) } pub struct Context<'a> { pub tokenizer: Tokenizer<'a>, pub scope_id: String, pub id_generator: IDGenerator, } fn parse_fragment<'a>( context: &mut Context<'a>, path: Vec, ) -> Result { let start = context.tokenizer.scanner.get_u16pos(); let mut children: Vec = vec![]; let mut raw_before = context.tokenizer.scanner.eat_whitespace(); while !context.tokenizer.scanner.is_eof() { let mut child_path = path.clone(); child_path.push(children.len().to_string()); children.push(parse_include_declaration(context, child_path, raw_before)?); raw_before = context.tokenizer.scanner.eat_whitespace(); } Ok(pc_ast::Node::Fragment(pc_ast::Fragment { id: context.id_generator.new_id(), children, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), })) } fn parse_include_declaration<'a>( context: &mut Context<'a>, path: Vec, raw_before: Option<&'a [u8]>, ) -> Result { let start = context.tokenizer.scanner.get_pos(); // Kinda ick, but cover case where last node is whitespace. let token = context.tokenizer.peek_eat_whitespace(1).or_else(|_| { context.tokenizer.scanner.set_pos(&start); context.tokenizer.peek(1) })?; match token { Token::CurlyOpen => parse_slot(context, &path, raw_before, 0), Token::LessThan => parse_tag(context, path, raw_before), Token::HtmlCommentOpen => parse_annotation(context, raw_before), Token::TagClose => { context.tokenizer.scanner.eat_whitespace(); let start = context.tokenizer.scanner.get_u16pos(); context.tokenizer.next_expect(Token::TagClose)?; let tag_name = parse_tag_name(context)?; context.tokenizer.next_expect(Token::GreaterThan)?; Err(ParseError::unexpected( "Closing tag doesn't have an open tag.".to_string(), start.range_from(context.tokenizer.scanner.get_u16pos()), )) } _ => { // reset pos to ensure text doesn't get chopped (e.g: `{children} text`) context.tokenizer.scanner.set_pos(&start); let value = get_buffer(&mut context.tokenizer, |tokenizer| { let tok = tokenizer.peek(1)?; Ok( tok != Token::CurlyOpen && tok != Token::LessThan && tok != Token::TagClose && tok != Token::HtmlCommentOpen, ) })? .to_string(); if value.len() == 0 { Err(ParseError::unexpected_token( start .to_u16() .range_from(context.tokenizer.scanner.get_u16pos()), )) } else { Ok(pc_ast::Node::Text(pc_ast::ValueObject { id: context.id_generator.new_id(), // keep raws on text to make it easier creating printers raws: BasicRaws::new(None, None), // want to include raws with text node to make it easier on rendering value: if let Some(before) = raw_before { format!("{}{}", str::from_utf8(before).unwrap(), value) } else { value.clone() }, range: start .to_u16() .range_from(context.tokenizer.scanner.get_u16pos()), })) } } } } fn parse_slot<'a>( context: &mut Context<'a>, path: &Vec, raw_before: Option<&'a [u8]>, index: usize, ) -> Result { let start = context.tokenizer.scanner.get_u16pos(); let omit_from_compilation = parse_omit_from_compilation(context)?; context.tokenizer.next_expect(Token::CurlyOpen)?; let script = parse_slot_script(context, Some((path, index)))?; Ok(pc_ast::Node::Slot(pc_ast::Slot { id: context.id_generator.new_id(), omit_from_compilation, raws: BasicRaws::new(raw_before, None), script, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), })) } fn parse_slot_script<'a>( context: &mut Context<'a>, id_seed_info_option: Option<(&Vec, usize)>, ) -> Result { let start = context.tokenizer.scanner.get_u16pos(); let mut js_tokenizer = JSTokenizer::new_from_scanner(context.tokenizer.scanner.clone()); let id_seed = if let Some((path, index)) = id_seed_info_option { format!("{}{}", path.join("-"), index) } else { "".to_string() }; let stmt = parse_js_with_tokenizer( &mut js_tokenizer, id_seed, context.scope_id.to_string().as_str(), ) .and_then(|script| { context .tokenizer .scanner .set_pos(&js_tokenizer.scanner.get_pos()); context.tokenizer.scanner.eat_whitespace(); context.tokenizer.next_expect(Token::CurlyClose)?; Ok(script) }) .or(Err(ParseError::unterminated( "Unterminated slot.".to_string(), start.range_from(context.tokenizer.scanner.get_u16pos()), ))); stmt } pub fn parse_annotation<'a>( context: &mut Context<'a>, raw_before: Option<&'a [u8]>, ) -> Result { let start = context.tokenizer.scanner.get_pos(); context.tokenizer.next()?; // eat HTML comment open let mut scanner = context.tokenizer.scanner.clone(); let mut annotation_tokenizer = AnnotationTokenizer::new_from_scanner(scanner); let annotation = parse_annotation_with_tokenizer( &mut annotation_tokenizer, &context.scope_id, |tokenizer| -> Result { Ok( tokenizer.peek(1)? == AnnotationToken::Byte(b'-') && tokenizer.peek(2)? == AnnotationToken::Byte(b'-') && tokenizer.peek(3)? == AnnotationToken::Byte(b'>'), ) }, )?; context .tokenizer .scanner .set_pos(&annotation_tokenizer.scanner.get_pos()); context.tokenizer.next()?; // eat --> Ok(pc_ast::Node::Comment(pc_ast::Comment { id: context.id_generator.new_id(), raws: BasicRaws::new(raw_before, None), range: Range::new(start.to_u16(), context.tokenizer.scanner.get_u16pos()), annotation, })) } pub fn parse_tag<'a>( context: &mut Context<'a>, path: Vec, raw_before: Option<&'a [u8]>, ) -> Result { let start = context.tokenizer.scanner.get_u16pos(); context.tokenizer.next_expect(Token::LessThan)?; parse_element(context, raw_before, path, start) } fn parse_element<'a>( context: &mut Context<'a>, el_raw_before: Option<&'a [u8]>, path: Vec, start: U16Position, ) -> Result { let tag_name_start = context.tokenizer.scanner.get_u16pos(); let tag_name = parse_tag_name(context)?; let tag_name_end = context.tokenizer.scanner.get_u16pos(); let tag_name_range = Range::new(tag_name_start, tag_name_end); let attributes = parse_attributes(context, &path)?; if tag_name == "style" { parse_next_style_element_parts(tag_name_range, attributes, el_raw_before, context, start) } else if tag_name == "script" { parse_next_script_element_parts( tag_name_range, attributes, el_raw_before, context, path, start, ) } else { parse_next_basic_element_parts( tag_name, tag_name_range, attributes, el_raw_before, context, path, start, ) } } fn parse_next_basic_element_parts<'a>( tag_name: String, tag_name_range: Range, attributes: Vec, el_raw_before: Option<&'a [u8]>, context: &mut Context<'a>, path: Vec, start: U16Position, ) -> Result { let mut children: Vec = vec![]; context.tokenizer.scanner.eat_whitespace(); let mut end = context.tokenizer.scanner.get_u16pos(); match context.tokenizer.peek(1)? { Token::SelfTagClose => { context.tokenizer.next()?; end = context.tokenizer.scanner.get_u16pos(); } Token::GreaterThan => { context.tokenizer.next()?; end = context.tokenizer.scanner.get_u16pos(); let mut raw_before = context.tokenizer.scanner.eat_whitespace(); while !context.tokenizer.scanner.is_eof() && context.tokenizer.peek_eat_whitespace(1)? != Token::TagClose { let mut child_path = path.clone(); child_path.push(children.len().to_string()); children.push(parse_include_declaration(context, child_path, raw_before)?); raw_before = context.tokenizer.scanner.eat_whitespace(); } parse_close_tag(tag_name.as_str(), context, &start, &end)?; } _ => { return Err(ParseError::unexpected_token( start.range_from(context.tokenizer.scanner.get_u16pos()), )) } } let el = pc_ast::Element { raws: pc_ast::ElementRaws::new(el_raw_before.unwrap_or(b"")), id: get_element_id(context, path), tag_name_range, open_tag_range: start.range_from(end), range: start.range_from(context.tokenizer.scanner.get_u16pos()), tag_name: tag_name.to_string(), attributes, children, }; Ok(pc_ast::Node::Element(el)) } fn get_element_id(context: &Context, path: Vec) -> String { let buff = format!("{}{}", context.scope_id, path.join("-")); format!("{:x}", crc32::checksum_ieee(buff.as_bytes())).to_string() } fn parse_next_style_element_parts<'a>( tag_name_range: Range, attributes: Vec, raw_before: Option<&'a [u8]>, context: &mut Context<'a>, start: U16Position, ) -> Result { context.tokenizer.next_expect(Token::GreaterThan)?; // eat > let end = context.tokenizer.scanner.get_u16pos(); let mut css_tokenizer = CSSTokenizer::new_from_scanner(context.tokenizer.scanner.clone()); let seed = context.id_generator.new_seed(); let sheet = parse_css_with_tokenizer( &mut css_tokenizer, seed.as_str(), |tokenizer| -> Result { Ok(tokenizer.peek(1)? == CSSToken::Byte(b'<') && tokenizer.peek(2)? == CSSToken::Byte(b'/')) }, )?; context .tokenizer .scanner .set_pos(&css_tokenizer.scanner.get_pos()); // TODO - assert tokens equal these parse_close_tag("style", context, &start, &end)?; Ok(pc_ast::Node::StyleElement(pc_ast::StyleElement { id: context.id_generator.new_id(), raws: pc_ast::ElementRaws::new(raw_before.unwrap_or(b"")), attributes, sheet, range: start.range_from(context.tokenizer.scanner.get_u16pos()), })) } fn parse_close_tag<'a, 'b>( tag_name: &'a str, context: &mut Context<'b>, start: &U16Position, end: &U16Position, ) -> Result<(), ParseError> { let end_tag_name_start = context.tokenizer.scanner.get_u16pos(); context.tokenizer.scanner.eat_whitespace(); context .tokenizer .next_expect(Token::TagClose) .or(Err(ParseError::unterminated( "Unterminated element.".to_string(), start.range_from(end.clone()), )))?; parse_tag_name(context) // TODO - assert tag name .and_then(|end_tag_name| { if tag_name != end_tag_name { Err(ParseError::unterminated( "Incorrect closing tag.".to_string(), end_tag_name_start.range_from(context.tokenizer.scanner.get_u16pos()), )) } else { Ok(()) } })?; context .tokenizer .next_expect(Token::GreaterThan) .or(Err(ParseError::unterminated( "Unterminated element.".to_string(), start.range_from(end.clone()), )))?; Ok(()) } fn parse_next_script_element_parts<'a>( tag_name_range: Range, attributes: Vec, raw_before: Option<&'a [u8]>, context: &mut Context<'a>, path: Vec, start: U16Position, ) -> Result { context.tokenizer.next_expect(Token::GreaterThan)?; // eat > let end = context.tokenizer.scanner.get_u16pos(); get_buffer(&mut context.tokenizer, |tokenizer| { Ok(tokenizer.peek(1)? != Token::TagClose) })?; parse_close_tag("script", context, &start, &end)?; Ok(pc_ast::Node::Element(pc_ast::Element { raws: pc_ast::ElementRaws::new(raw_before.unwrap_or(b"")), id: path .iter() .map(|i| i.to_string()) .collect::>() .join("-"), tag_name_range, open_tag_range: start.range_from(end), range: start.range_from(context.tokenizer.scanner.get_u16pos()), tag_name: "script".to_string(), attributes, children: vec![], })) } fn parse_tag_name<'a>(context: &mut Context<'a>) -> Result { Ok( get_buffer(&mut context.tokenizer, |tokenizer| { Ok(matches!( tokenizer.peek(1)?, Token::Word(_) | Token::Minus | Token::Dot | Token::Dollar | Token::Byte(b'_') | Token::Number(_) )) })? .to_string(), ) } fn parse_attributes<'a>( context: &mut Context<'a>, path: &Vec, ) -> Result, ParseError> { let mut attributes: Vec = vec![]; loop { context.tokenizer.scanner.eat_whitespace(); match context.tokenizer.peek(1)? { Token::SelfTagClose | Token::GreaterThan => break, _ => { attributes.push(parse_attribute(context, path, attributes.len())?); } } } Ok(attributes) } fn parse_attribute<'a>( context: &mut Context<'a>, path: &Vec, index: usize, ) -> Result { if context.tokenizer.peek(1)? == Token::CurlyOpen { parse_shorthand_attribute(context) } else { parse_key_value_attribute(context, path, index) } } fn parse_omit_from_compilation<'a>(context: &mut Context<'a>) -> Result { Ok(if context.tokenizer.peek(1)? == Token::Bang { context.tokenizer.next()?; true } else { false }) } fn parse_shorthand_attribute<'a>( context: &mut Context<'a>, ) -> Result { let omit_from_compilation = parse_omit_from_compilation(context)?; let start = context.tokenizer.scanner.get_u16pos(); context.tokenizer.next_expect(Token::CurlyOpen)?; if context.tokenizer.peek(1)? == Token::Spread { context.tokenizer.next_expect(Token::Spread)?; let script = parse_slot_script(context, None)?; Ok(pc_ast::Attribute::SpreadAttribute( pc_ast::SpreadAttribute { id: context.id_generator.new_id(), omit_from_compilation, script, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), }, )) } else { let reference = parse_slot_script(context, None)?; Ok(pc_ast::Attribute::ShorthandAttribute( pc_ast::ShorthandAttribute { id: context.id_generator.new_id(), reference, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), }, )) } } fn parse_key_value_attribute<'a>( context: &mut Context<'a>, path: &Vec, index: usize, ) -> Result { let start = context.tokenizer.scanner.get_u16pos(); let name = parse_tag_name(context)?; if name.len() == 0 { return Err(ParseError::unexpected_token( start.range_from(context.tokenizer.scanner.get_u16pos()), )); } if context.tokenizer.peek(1)? == Token::Colon { context.tokenizer.next()?; // eat : let binding_name = parse_tag_name(context)?; let mut value = None; if context.tokenizer.peek(1)? == Token::Equals { context.tokenizer.next()?; // eat = value = Some(parse_attribute_value(context, path, index)?); // Fix https://github.com/paperclipui/paperclip/issues/306 // Keep in case we want to turn this back on. } else { return Err(ParseError::unexpected_token( start.range_from(context.tokenizer.scanner.get_u16pos()), )); } Ok(pc_ast::Attribute::PropertyBoundAttribute( pc_ast::PropertyBoundAttribute { id: context.id_generator.new_id(), name, binding_name, value, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), }, )) } else if context.tokenizer.peek(1)? == Token::Equals { context.tokenizer.next()?; // eat = let value = Some(parse_attribute_value(context, path, index)?); Ok(pc_ast::Attribute::KeyValueAttribute( pc_ast::KeyValueAttribute { id: context.id_generator.new_id(), name, value, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), }, )) } else if context.tokenizer.peek(1)? == Token::Whitespace { context.tokenizer.next()?; // eat WS Ok(pc_ast::Attribute::KeyValueAttribute( pc_ast::KeyValueAttribute { id: context.id_generator.new_id(), name, value: None, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), }, )) } else if matches!( context.tokenizer.peek(1)?, Token::SelfTagClose | Token::GreaterThan ) { Ok(pc_ast::Attribute::KeyValueAttribute( pc_ast::KeyValueAttribute { id: context.id_generator.new_id(), name, value: None, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), }, )) } else { Err(ParseError::unexpected_token( start.range_from(context.tokenizer.scanner.get_u16pos()), )) } } fn parse_attribute_value<'a>( context: &mut Context<'a>, path: &Vec, index: usize, ) -> Result { let pos = context.tokenizer.scanner.get_u16pos(); let parts: Vec = vec![]; match context.tokenizer.peek(1)? { Token::SingleQuote | Token::DoubleQuote => parse_attribute_string_value(context), Token::CurlyOpen => parse_attribute_slot(context, path, index), _ => Err(ParseError::unexpected_token( pos.range_from(context.tokenizer.scanner.get_u16pos()), )), } } fn parse_attribute_string_value<'a>( context: &mut Context<'a>, ) -> Result { let pos = context.tokenizer.scanner.get_u16pos(); let mut parts: Vec = vec![]; let quote = context.tokenizer.next()?; let inner_value_start_pos = context.tokenizer.scanner.get_u16pos(); while !context.tokenizer.scanner.is_eof() { let curr = context.tokenizer.peek(1)?; if curr == quote { break; } if curr == Token::Pierce || curr == Token::Dollar { context.tokenizer.next()?; // eat $ let class_name = get_buffer(&mut context.tokenizer, |tokenizer| { let tok = tokenizer.peek(1)?; Ok( !matches!( tok, Token::Whitespace | Token::Pierce | Token::Dollar | Token::CurlyOpen ) && tok != quote, ) })? .to_string(); parts.push(pc_ast::AttributeDynamicStringPart::ClassNamePierce( pc_ast::AttributeDynamicStringClassNamePierce { id: context.id_generator.new_id(), class_name, range: pos.range_from(context.tokenizer.scanner.get_u16pos()), }, )); } else if curr == Token::CurlyOpen { context.tokenizer.next_expect(Token::CurlyOpen)?; let script = parse_slot_script(context, None)?; parts.push(pc_ast::AttributeDynamicStringPart::Slot(script)); } else { let start = context.tokenizer.scanner.get_u16pos(); let value = get_buffer(&mut context.tokenizer, |tokenizer| { let tok = tokenizer.peek(1)?; Ok(!matches!(tok, Token::Pierce | Token::Dollar | Token::CurlyOpen) && tok != quote) })? .to_string(); parts.push(pc_ast::AttributeDynamicStringPart::Literal({ core_ast::StringLiteral { id: context.id_generator.new_id(), value, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), } })); } } let inner_value_end_pos = context.tokenizer.scanner.get_u16pos(); context .tokenizer .next_expect(quote) .or(Err(ParseError::unterminated( "Unterminated string literal.".to_string(), pos.range_from(context.tokenizer.scanner.get_u16pos()), )))?; let range = Range::new(inner_value_start_pos, inner_value_end_pos); if parts.len() == 0 { return Ok(pc_ast::AttributeValue::String(core_ast::StringLiteral { id: context.id_generator.new_id(), value: "".to_string(), range, })); } if parts.len() == 1 { if let pc_ast::AttributeDynamicStringPart::Literal(value) = &parts[0] { return Ok(pc_ast::AttributeValue::String(core_ast::StringLiteral { id: context.id_generator.new_id(), value: value.value.clone(), range, })); } } return Ok(pc_ast::AttributeValue::DyanmicString( pc_ast::AttributeDynamicStringValue { id: context.id_generator.new_id(), values: parts, range, }, )); } fn parse_attribute_slot<'a>( context: &mut Context<'a>, path: &Vec, index: usize, ) -> Result { let start = context.tokenizer.scanner.get_u16pos(); context.tokenizer.next_expect(Token::CurlyOpen)?; let script = parse_slot_script(context, Some((path, index)))?; Ok(pc_ast::AttributeValue::Slot(pc_ast::AttributeSlotValue { id: context.id_generator.new_id(), script, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), })) } fn parse_attribute_string<'a>( context: &mut Context<'a>, ) -> Result { let start = context.tokenizer.scanner.get_u16pos(); let quote = context.tokenizer.next()?; let inner_start = context.tokenizer.scanner.get_u16pos(); let inner_end = context.tokenizer.scanner.get_u16pos(); get_buffer(&mut context.tokenizer, |tokenizer| { Ok(tokenizer.peek(1)? != quote) }) .and_then(|value| { context.tokenizer.next_expect(quote)?; Ok(value) }) .or(Err(ParseError::unterminated( "Unterminated string literal.".to_string(), start.range_from(context.tokenizer.scanner.get_u16pos()), ))) .and_then(|value| { Ok(pc_ast::AttributeValue::String(core_ast::StringLiteral { id: context.id_generator.new_id(), value: value.to_string(), range: Range::new(inner_start, inner_end), })) }) } #[cfg(test)] mod tests { use super::*; #[test] fn can_smoke_parse_various_nodes() { let source = " text {10.10.10}
    {block} \n
    \n
    "; parse(source, "id", "url").unwrap(); } } ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/annotation_evaluator.rs ================================================ pub fn evaluate ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/cache.rs ================================================ use super::virt as pc_virt; use crate::css::runtime::virt as css_virt; use std::collections::HashMap; pub struct Cache { pub style_entries: HashMap, pub node_entries: HashMap, } impl Cache { pub fn new() -> Cache { Cache { style_entries: HashMap::new(), node_entries: HashMap::new(), } } pub fn remove(&mut self, uri: &String) { self.style_entries.remove(uri); self.node_entries.remove(uri); } } ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/diff.rs ================================================ use super::mutation::*; use super::virt::{Element, Fragment, Node, StyleElement, Text}; use std::cmp::{max, min}; /* NOTE: diffing is pretty dumb now. May want to make it smarter later on. TODOS: - [ ] need to diff & patch expression info */ struct Context<'a> { node_path: Vec, mutations: &'a mut Vec, } pub fn diff(a: &Node, b: &Node) -> Vec { let mut mutations: Vec = vec![]; let mut context = Context { mutations: &mut mutations, node_path: vec![], }; diff_node(a, b, &mut context); mutations } fn diff_node<'a>(a: &Node, b: &Node, context: &mut Context<'a>) { if let Node::Element(element1) = a { if let Node::Element(element2) = b { return diff_element(element1, element2, context); } } else if let Node::Fragment(fragment1) = a { if let Node::Fragment(fragment2) = b { return diff_fragment(fragment1, fragment2, context); } } else if let Node::Text(text1) = a { if let Node::Text(text2) = b { return diff_text(text1, text2, context); } } else if let Node::StyleElement(style_element1) = a { if let Node::StyleElement(style_element2) = b { return diff_style_element(style_element1, style_element2, context); } } context.mutations.push(Mutation::new( context.node_path.clone(), Action::ReplaceNode(ReplaceNode { replacement: b.clone(), }), )); } fn diff_element<'a>(a: &Element, b: &Element, context: &mut Context<'a>) { if (a.annotations != b.annotations) { context.mutations.push(Mutation::new( context.node_path.clone(), Action::SetAnnotations(SetAnnotations { value: b.annotations.clone(), }), )); } if (a.source_id != b.source_id) { context.mutations.push(Mutation::new( context.node_path.clone(), Action::SetElementSourceId(SetElementSourceId { value: b.source_id.to_string(), }), )); } if (a.source_info != b.source_info) { context.mutations.push(Mutation::new( context.node_path.clone(), Action::SetElementSourceInfo(SetElementSourceInfo { value: b.source_info.clone(), }), )); } if a.tag_name != b.tag_name { context.mutations.push(Mutation::new( context.node_path.clone(), Action::ReplaceNode(ReplaceNode { replacement: Node::Element(b.clone()), }), )); return; } for (name, value) in a.attributes.iter() { let value2_option = b.attributes.get(name); match value2_option { Some(value2) => { if value != value2 { context.mutations.push(Mutation::new( context.node_path.clone(), Action::SetAttribute(SetAttribute { name: name.clone(), value: value2.clone(), }), )); } } None => { context.mutations.push(Mutation::new( context.node_path.clone(), Action::RemoveAttribute(RemoveAttribute { name: name.clone() }), )); } } } for (name, value) in b.attributes.iter() { let value1_option = a.attributes.get(name); match value1_option { None => { context.mutations.push(Mutation::new( context.node_path.clone(), Action::SetAttribute(SetAttribute { name: name.clone(), value: value.clone(), }), )); } _ => {} } } diff_children(&a.children, &b.children, context); } fn diff_fragment<'a>(a: &Fragment, b: &Fragment, context: &mut Context<'a>) { diff_children(&a.children, &b.children, context); } fn diff_text<'a>(a: &Text, b: &Text, context: &mut Context<'a>) { if (a.annotations != b.annotations) { context.mutations.push(Mutation::new( context.node_path.clone(), Action::SetAnnotations(SetAnnotations { value: b.annotations.clone(), }), )); } if a.value != b.value { context.mutations.push(Mutation::new( context.node_path.clone(), Action::SetText(SetText { value: b.value.clone(), }), )); } } fn diff_children<'a>(a: &Vec, b: &Vec, context: &mut Context<'a>) { for i in 0..min(a.len(), b.len()) { let mut node_path = context.node_path.clone(); node_path.push(i); diff_node( &a[i], &b[i], &mut Context { node_path, mutations: context.mutations, }, ); } if a.len() > b.len() { for i in b.len()..a.len() { context.mutations.push(Mutation::new( context.node_path.clone(), Action::DeleteChild(DeleteChild { index: b.len() }), )) } } else if b.len() > a.len() { for i in a.len()..b.len() { context.mutations.push(Mutation::new( context.node_path.clone(), Action::InsertChild(InsertChild { index: i, child: b[i].clone(), }), )) } } } fn diff_style_element<'a>(a: &StyleElement, b: &StyleElement, context: &mut Context<'a>) { // will want to diff & patch styles later on context.mutations.push(Mutation::new( context.node_path.clone(), Action::ReplaceNode(ReplaceNode { replacement: Node::StyleElement(b.clone()), }), )); } ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/evaluator.rs ================================================ use super::super::ast; use super::export::{ComponentExport, Exports, Property}; use super::virt; use crate::annotation::ast as annotation_ast; use crate::base::ast::Range; use crate::base::runtime::RuntimeError; use crate::base::utils::{get_document_id, get_document_style_public_scope, is_relative_path}; use crate::core::ast as core_ast; use crate::core::ast::ExprVisitor; use crate::core::eval::DependencyEvalInfo; use crate::core::graph::{Dependency, DependencyContent, DependencyGraph}; use crate::core::vfs::VirtualFileSystem; // use crate::css::runtime::evaluator::{evaluate as evaluate_css, EvalInfo as CSSEvalInfo}; use crate::core::ast::Expr; use crate::core::eval_utils::resolve_asset; use crate::core::id_generator::IDGenerator; use crate::css::ast as css_ast; use crate::css::runtime::evaluator::{evaluate_expr as evaluate_css_expr, EvalInfo as CSSEvalInfo}; use crate::css::runtime::export as css_export; use crate::css::runtime::virt as css_virt; use crate::script::ast as script_ast; use crate::script::runtime::evaluator::evaluate as evaluate_js; use crate::script::runtime::virt as script_virt; use regex::Regex; use serde::Serialize; use std::collections::{BTreeMap, HashSet}; use std::iter::FromIterator; #[derive(Debug, PartialEq, Serialize)] #[serde(tag = "kind")] pub enum EngineMode { SingleFrame, MultiFrame, } pub struct ElementSource { pub id: String, pub tag_name: String, } impl ElementSource { pub fn new_from_element(element: &ast::Element) -> ElementSource { ElementSource { id: element.id.to_string(), tag_name: element.tag_name.to_string(), } } } #[derive(Clone)] pub struct Context<'a> { pub graph: &'a DependencyGraph, pub id_generator: IDGenerator, pub vfs: &'a VirtualFileSystem, pub uri: &'a String, pub import_ids: HashSet<&'a String>, pub part_ids: HashSet<&'a String>, pub private_scope: String, pub injected_scopes: Vec, pub document_scopes: Vec, pub public_scope: String, pub import_scopes: BTreeMap, pub data: &'a script_virt::Value, pub render_call_stack: Vec<(String, RenderStrategy)>, pub evaluated_graph: &'a BTreeMap, pub mode: &'a EngineMode, pub include_used_exprs: bool, pub used_expr_ids: HashSet, } impl<'a> Context<'a> { pub fn get_current_render_strategy(&self) -> &(String, RenderStrategy) { self .render_call_stack .get(self.render_call_stack.len() - 1) .unwrap() } } #[derive(Clone, PartialEq, Debug)] pub enum RenderStrategy { Part(String), Auto, } #[derive(Debug, Clone, PartialEq, Serialize)] pub struct EvalInfo { // this is necessary so that consumers of EvalInfo can pull in all // style sheets #[serde(rename = "allImportedSheetUris")] pub all_imported_sheet_uris: Vec, // need to include the imported dependency URIs since they're a snapshot for the evaluated data, // and might not exist as AST if there's a parse error pub dependencies: BTreeMap, pub sheet: css_virt::CSSSheet, pub preview: virt::Node, pub exports: Exports, #[serde(rename = "usedExprIds")] pub used_expr_ids: Option>, } pub fn evaluate<'a>( uri: &String, graph: &'a DependencyGraph, vfs: &'a VirtualFileSystem, evaluated_graph: &'a BTreeMap, include_used_exprs: bool, mode: &EngineMode, id_seed: String, ) -> Result { let dep: &Dependency = graph.dependencies.get(uri).ok_or(RuntimeError::new( "URI not loaded".to_string(), uri, &Range::nil(), ))?; if let DependencyContent::Node(node_expr) = &dep.content { let data = script_virt::Value::Object(script_virt::Object::new(node_expr.get_id().to_string())); let mut context = create_context( node_expr, uri, graph, vfs, &data, vec![], evaluated_graph, include_used_exprs, mode, id_seed, ); let preview = wrap_as_fragment( evaluate_instance_node( node_expr, &mut context, RenderStrategy::Auto, false, 0, &None, None, )?, &mut context, ); let (sheet, css_exports) = evaluate_document_sheet(uri, node_expr, &mut context)?; Ok(EvalInfo { sheet, preview, all_imported_sheet_uris: graph.flatten_dependencies(uri), dependencies: dep.dependencies.clone(), used_expr_ids: if include_used_exprs { Some(context.used_expr_ids.clone()) } else { None }, exports: Exports { style: css_exports, components: collect_component_exports(&node_expr, &mut context)?, }, }) } else { Err(RuntimeError::new( "Incorrect file type".to_string(), uri, &Range::nil(), )) } } fn collect_component_exports<'a>( root: &ast::Node, context: &mut Context, ) -> Result, RuntimeError> { let mut exports: BTreeMap = BTreeMap::new(); let children = root.get_children(); if let Some(children) = children { for child in children.iter() { if let ast::Node::Element(element) = child { if ast::has_attribute("component", element) && ast::get_attribute_value("as", element) != None { let id = ast::get_attribute_value("as", element).ok_or(RuntimeError::new( "As must be present".to_string(), context.uri, &Range::nil(), ))?; let properties = collect_node_properties(child); if exports.contains_key(id) { return Err(RuntimeError::new( "Component name is already declared.".to_string(), context.uri, &element.range, )); } exports.insert( id.to_string(), ComponentExport { name: id.to_string(), properties, public: ast::get_attribute("export", element) != None, }, ); } } } } Ok(exports) } struct CollectNodePropVisitor { pub properties: BTreeMap, } impl<'a> ExprVisitor<'a> for CollectNodePropVisitor { fn visit_node(&mut self, node: &'a ast::Node) { match node { ast::Node::Element(element) => { for attr in &element.attributes { match attr { // ast::Attribute::KeyValueAttribute(kv_attr) => { if let Some(value) = &kv_attr.value { match value { // ast::AttributeValue::Slot(slot) => { add_script_property(&slot.script, &mut self.properties); } // ast::AttributeValue::DyanmicString(d_string) => { for val in &d_string.values { match val { ast::AttributeDynamicStringPart::Slot(slot) => { add_script_property(&slot, &mut self.properties); } ast::AttributeDynamicStringPart::ClassNamePierce(_) | ast::AttributeDynamicStringPart::Literal(_) => {} } } } ast::AttributeValue::String(_) => {} } } } // ast::Attribute::SpreadAttribute(spread) => { add_script_property(&spread.script, &mut self.properties); } // ast::Attribute::PropertyBoundAttribute(p_attr) => { add_property(&p_attr.binding_name, true, &mut self.properties); } // ast::Attribute::ShorthandAttribute(s_attr) => { add_script_property(&s_attr.reference, &mut self.properties); } } } } ast::Node::Slot(slot) => { add_script_property(&slot.script, &mut self.properties); } _ => {} } } fn should_continue(&self) -> bool { return true; } fn visit_script_expression(&mut self, _rule: &'a script_ast::Expression) {} fn visit_css_rule(&mut self, _rule: &'a css_ast::Rule) {} fn visit_css_decl(&mut self, _decl: &'a css_ast::Declaration) {} fn visit_css_sheet(&mut self, _decl: &'a css_ast::Sheet) {} fn visit_attr(&mut self, _attr: &'a ast::Attribute) {} fn visit_str(&mut self, _attr: &'a core_ast::StringLiteral) {} } fn collect_node_properties<'a>(node: &ast::Node) -> BTreeMap { let mut visitor = CollectNodePropVisitor { properties: BTreeMap::new(), }; node.walk(&mut visitor); visitor.properties } fn add_script_property( script: &script_ast::Expression, properties: &mut BTreeMap, ) { match script { script_ast::Expression::Reference(reference) => { let part = reference.path.get(0).unwrap(); add_property(&part.name, part.optional, properties); } script_ast::Expression::Conjunction(conjunction) => { add_script_property(&conjunction.left, properties); add_script_property(&conjunction.right, properties); } script_ast::Expression::Group(group) => { add_script_property(&group.expression, properties); } script_ast::Expression::Node(node) => { properties.extend(collect_node_properties(&node)); } _ => {} }; } fn add_property(name: &String, optional: bool, properties: &mut BTreeMap) { let optional = if let Some(prop) = properties.get(name) { prop.optional } else { optional }; properties.insert( name.to_string(), Property { name: name.to_string(), optional, }, ); } fn wrap_as_fragment<'a>(node_option: Option, context: &'a mut Context) -> virt::Node { if let Some(node) = node_option { match node { virt::Node::Fragment(fragment) => virt::Node::Fragment(fragment), _ => virt::Node::Fragment(virt::Fragment { id: context.id_generator.new_id(), source_id: use_expr_id(node.get_source_id(), context), children: vec![node], }), } } else { virt::Node::Fragment(virt::Fragment { id: context.id_generator.new_id(), source_id: "".to_string(), children: vec![], }) } } pub fn get_instance_target_node<'a>( node_expr: &'a ast::Node, render_strategy: &RenderStrategy, imported: bool, ) -> Option<&'a ast::Node> { let target_node_option = match render_strategy { RenderStrategy::Part(id) => find_child(node_expr, |child| { ast::get_attribute_value("as", child) == Some(&id) && ast::has_attribute("component", child) && (!imported || ast::has_attribute("export", child)) }), RenderStrategy::Auto => find_child(node_expr, |child| ast::has_attribute("preview", child)), }; if let Some(target_node) = target_node_option { Some(target_node) } else if render_strategy == &RenderStrategy::Auto { Some(node_expr) } else { None } } fn find_child(parent: &ast::Node, test: TTest) -> Option<&ast::Node> where TTest: Fn(&ast::Element) -> bool, { match parent.get_children() { Some(children) => children.iter().find(|child| { if let ast::Node::Element(element) = child { if test(element) { return true; } } false }), None => None, } } fn evaluate_document_sheet<'a>( uri: &String, entry_expr: &ast::Node, context: &'a mut Context, ) -> Result<(css_virt::CSSSheet, css_export::Exports), RuntimeError> { let mut sheet = css_virt::CSSSheet { id: context.id_generator.new_id(), rules: vec![], }; let mut css_exports: css_export::Exports = css_export::Exports::new(); evaluate_node_sheet(uri, None, entry_expr, &mut sheet, &mut css_exports, context)?; Ok((sheet, css_exports)) } fn evaluate_node_sheet<'a>( uri: &String, parent: Option<&ast::Node>, current: &ast::Node, sheet: &'a mut css_virt::CSSSheet, css_exports: &'a mut css_export::Exports, context: &'a mut Context, ) -> Result<(), RuntimeError> { let private_scope = get_document_id(uri); let public_scope = get_document_style_public_scope(uri); let element_scope = if let Some(parent) = parent { if let ast::Node::Element(element) = parent { Some(( element.id.to_string(), is_component_instance(element, context), )) } else { None } } else { None }; if let ast::Node::StyleElement(style_element) = ¤t { let info = evaluate_css_expr( &style_element.sheet, uri, &private_scope, &public_scope, element_scope, context.import_scopes.clone(), context.vfs, context.graph, &context.evaluated_graph, Some(&css_exports), false, context.id_generator.new_id(), )?; match info { CSSEvalInfo { sheet: child_sheet, exports: child_exports, } => { sheet.extend(child_sheet); css_exports.extend(&child_exports); } } } let children_option = current.get_children(); if let Some(children) = children_option { // style elements are only allowed in root, so no need to traverse for child in children { evaluate_node_sheet(uri, Some(¤t), child, sheet, css_exports, context)?; } } // scan slots for styles. Holy nested check batman. if let ast::Node::Element(element) = current { for attribute in &element.attributes { if let ast::Attribute::KeyValueAttribute(kv) = attribute { if let Some(value) = &kv.value { if let ast::AttributeValue::Slot(slot) = value { traverse_script_expr(&slot.script, &mut |expr| { if let script_ast::Expression::Node(node) = expr { evaluate_node_sheet(uri, Some(¤t), &*node, sheet, css_exports, context)?; } Ok(()) })?; } } } } } if let ast::Node::Slot(slot) = current { traverse_script_expr(&slot.script, &mut |expr| { if let script_ast::Expression::Node(node) = expr { evaluate_node_sheet(uri, Some(¤t), &*node, sheet, css_exports, context)?; } Ok(()) })?; } Ok(()) } pub fn traverse_script_expr( current: &script_ast::Expression, each: &mut TEach, ) -> Result<(), RuntimeError> where TEach: FnMut(&script_ast::Expression) -> Result<(), RuntimeError>, { each(current)?; match current { script_ast::Expression::Conjunction(expr) => { traverse_script_expr(&expr.left, each)?; traverse_script_expr(&expr.right, each)?; } script_ast::Expression::Group(expr) => { traverse_script_expr(&expr.expression, each)?; } script_ast::Expression::Not(expr) => { traverse_script_expr(&expr.expression, each)?; } script_ast::Expression::Array(expr) => { for value in &expr.values { traverse_script_expr(&value, each)?; } } script_ast::Expression::Node(_) => {} script_ast::Expression::Object(expr) => { for property in &expr.properties { traverse_script_expr(&property.value, each)?; } } _ => {} } Ok(()) } pub fn evaluate_instance_node<'a>( node_expr: &ast::Node, context: &'a mut Context, render_strategy: RenderStrategy, imported: bool, depth: u32, annotations: &Option, instance_source: Option, ) -> Result, RuntimeError> { context .render_call_stack .push((context.uri.to_string(), render_strategy.clone())); let target_option = get_instance_target_node(node_expr, &render_strategy, imported); if let Some(target) = target_option { evaluate_node(target, depth, instance_source, annotations, context) } else { Ok(None) } } fn create_context<'a>( node_expr: &'a ast::Node, uri: &'a String, graph: &'a DependencyGraph, vfs: &'a VirtualFileSystem, data: &'a script_virt::Value, render_call_stack: Vec<(String, RenderStrategy)>, evaluated_graph: &'a BTreeMap, include_used_exprs: bool, mode: &'a EngineMode, id_seed: String, ) -> Context<'a> { let private_scope = get_document_id(uri); let public_scope = get_document_style_public_scope(uri); let injected_scopes = get_injected_scoped(node_expr, graph.dependencies.get(uri).unwrap()); let mut document_scopes: Vec = vec![private_scope.clone(), public_scope.clone()]; document_scopes.extend(injected_scopes.clone()); Context { graph, uri, vfs, render_call_stack, evaluated_graph, import_ids: HashSet::from_iter(ast::get_import_ids(node_expr)), injected_scopes, document_scopes, import_scopes: get_import_scopes(graph.dependencies.get(uri).unwrap()), part_ids: HashSet::from_iter(ast::get_part_ids(node_expr)), private_scope, public_scope, id_generator: IDGenerator::new(id_seed), data, mode, include_used_exprs, used_expr_ids: HashSet::new(), } } pub fn get_injected_scoped<'a>(root_expr: &'a ast::Node, entry: &Dependency) -> Vec { let mut scopes: Vec = vec![]; for import in ast::get_imports(root_expr) { if ast::has_attribute("inject-styles", &import) { if let Some(src) = ast::get_attribute_value("src", &import) { scopes.push(get_document_style_public_scope( entry.dependency_uri_maps.get(src).unwrap(), )); } } } scopes } pub fn get_import_scopes<'a>(entry: &Dependency) -> BTreeMap { let mut scopes = BTreeMap::new(); for (id, uri) in &entry.dependencies { scopes.insert(id.to_string(), get_document_style_public_scope(uri)); } scopes } pub fn evaluate_node<'a>( node_expr: &ast::Node, depth: u32, instance_source: Option, annotations: &Option, context: &'a mut Context, ) -> Result, RuntimeError> { if context.mode == &EngineMode::MultiFrame && !is_frame_visible(annotations) { return Ok(None); } match &node_expr { ast::Node::Element(el) => evaluate_element(&el, depth, instance_source, annotations, context), ast::Node::StyleElement(el) => { return evaluate_style_element(&el, context); } ast::Node::Text(text) => { // skip pure whitespace (this should only happen with new lines). This is necessary // particularly for annotations // if text.value.trim() == "" { // return Ok(None); // } return Ok(Some(virt::Node::Text(virt::Text { id: context.id_generator.new_id(), source_id: use_expr_id(node_expr.get_id(), context), annotations: annotations.clone(), value: text.value.to_string(), }))); } ast::Node::Slot(slot) => evaluate_slot(&slot, depth, context), ast::Node::Fragment(el) => evaluate_fragment(&el, depth, context), ast::Node::Comment(_) => Ok(None), } } // pub fn get_element_scope<'a>(element: &ast::Element, context: &mut Context) -> String { // let buff = format!("{}{}", element.id); // format!("{:x}", crc32::checksum_ieee(buff.as_bytes())).to_string() // } fn is_frame_visible(annotations: &Option) -> bool { let visible = annotations .as_ref() .and_then(|obj| -> Option<&script_virt::Value> { obj.values.get("frame") }) .and_then(|frame_value| match frame_value { script_virt::Value::Object(frame) => Some(frame), _ => None, }) .and_then(|frame| frame.values.get("visible")) .and_then(|visible_value| match visible_value { script_virt::Value::Boolean(visible) => Some(visible.value), _ => Some(true), }); visible != Some(false) } fn evaluate_element<'a>( element: &ast::Element, depth: u32, instance_source: Option, annotations: &Option, context: &'a mut Context, ) -> Result, RuntimeError> { add_used_expr_id(&element.id, context); match element.tag_name.as_str() { "import" => evaluate_import_element(element, context), "script" | "property" | "logic" => Ok(None), _ => { if ast::has_attribute("component", element) { if let Some(id) = ast::get_attribute_value("as", element) { if context.get_current_render_strategy() != &( context.uri.to_string(), RenderStrategy::Part(id.to_string()), ) { if depth != 1 { return Err(RuntimeError::new( "Components need to be defined at the root.".to_string(), context.uri, &element.range, )); } // components should render with multi-frame mode if context.mode != &EngineMode::MultiFrame { return Ok(None); } } } } if context.import_ids.contains(&ast::get_tag_name(&element)) { let result = evaluate_imported_component( element, instance_source.or(Some(ElementSource::new_from_element(element))), depth, annotations, context, ); if Ok(None) == result { return Err(RuntimeError::new( "Unable to find component, or it's not exported.".to_string(), &context.uri, &element.open_tag_range, )); } result } else if context.part_ids.contains(&element.tag_name) { evaluate_part_instance_element( element, instance_source.or(Some(ElementSource::new_from_element(element))), depth, annotations, context, ) } else { // fragments should be preserved if in multi frame mode if root if element.tag_name == "fragment" && (context.mode != &EngineMode::MultiFrame || depth > 1) { evaluate_children_as_fragment(&element.children, depth, &element.id, context) } else { evaluate_native_element(element, depth, instance_source, annotations, context) } } } } } fn evaluate_slot<'a>( slot: &ast::Slot, depth: u32, context: &'a mut Context, ) -> Result, RuntimeError> { use_expr_id(&slot.id, context); assert_slot_restrictions(&slot.range, context)?; let script = &slot.script; let mut script_value = evaluate_js(script, depth + 1, context)?; // if array of values, then treat as document fragment if let script_virt::Value::Array(ary) = &mut script_value { let mut children = vec![]; for item in ary.values.drain(0..) { if let script_virt::Value::Node(child) = item { children.push(child); } else { children.push(virt::Node::Text(virt::Text { id: context.id_generator.new_id(), source_id: use_expr_id(item.get_source_id(), context), annotations: None, value: item.to_string(), })) } } return Ok(Some(virt::Node::Fragment(virt::Fragment { id: context.id_generator.new_id(), source_id: use_expr_id(&ary.source_id, context), children, }))); } else if let script_virt::Value::Node(node) = script_value { return Ok(Some(node)); } if !matches!(script_value, script_virt::Value::Undefined(_)) { Ok(Some(virt::Node::Text(virt::Text { id: context.id_generator.new_id(), source_id: use_expr_id(script_value.get_source_id(), context), annotations: None, value: if script_value.truthy() || script_value.is_number() { script_value.to_string() } else { "".to_string() }, }))) } else { Ok(Some(virt::Node::Slot(virt::Slot { id: context.id_generator.new_id(), source_id: use_expr_id(&script_value.get_source_id(), context), }))) } } pub fn evaluate_imported_component<'a>( element: &ast::Element, instance_source: Option, depth: u32, annotations: &Option, context: &'a mut Context, ) -> Result, RuntimeError> { let self_dep = &context.graph.dependencies.get(context.uri).unwrap(); let tag_name = &ast::get_tag_name(element); let dep_uri = &self_dep.dependencies.get(tag_name).unwrap(); let namespace_option = ast::get_tag_namespace(element); evaluate_component_instance( element, if let Some(part) = namespace_option { RenderStrategy::Part(part) } else { RenderStrategy::Part("default".to_string()) }, true, depth, instance_source, annotations, dep_uri, context, ) } fn add_used_expr_id<'a>(expr_id: &String, context: &'a mut Context) { if context.include_used_exprs { context.used_expr_ids.insert(expr_id.to_string()); } } pub fn use_expr_id<'a>(expr_id: &'a String, context: &'a mut Context) -> String { let ret = expr_id.to_string(); add_used_expr_id(&ret, context); ret } fn in_render_stack<'a>(strategy: &RenderStrategy, context: &'a mut Context) -> bool { context .render_call_stack .iter() .any(|(uri, part)| uri == context.uri && part == strategy) } fn check_instance_loop<'a>( strategy: &RenderStrategy, element: &ast::Element, context: &'a mut Context, ) -> Result<(), RuntimeError> { let tag_option = match strategy { RenderStrategy::Part(id) => Some(id.to_string()), RenderStrategy::Auto => None, }; if let Some(tag) = tag_option { if in_render_stack(strategy, context) { return Err(RuntimeError { uri: context.uri.to_string(), message: format!( "Can't call <{} /> here since this causes an infinite loop!", tag ) .to_string(), range: element.open_tag_range.clone(), }); } else { Ok(()) } } else { Ok(()) } } fn evaluate_part_instance_element<'a>( element: &ast::Element, instance_source: Option, depth: u32, annotations: &Option, context: &'a mut Context, ) -> Result, RuntimeError> { let self_dep = &context.graph.dependencies.get(context.uri).unwrap(); if let DependencyContent::Node(_root_node) = &self_dep.content { evaluate_component_instance( element, RenderStrategy::Part(element.tag_name.to_string()), false, depth, instance_source, annotations, context.uri, context, ) } else { // This should _never_ happen Err(RuntimeError::unknown(context.uri)) } } fn create_component_instance_data<'a>( instance_element: &ast::Element, depth: u32, context: &'a mut Context, ) -> Result { let mut data = evaluate_attributes(instance_element, false, depth, context)?; let mut script_children = script_virt::Array::new(instance_element.id.to_string()); let (ret_children, contains_style) = evaluate_children(&instance_element.children, depth, context)?; if contains_style { let class_name_option = data.values.get("class"); let scope_class_name = &instance_element.id; let new_class_name = if let Some(class_name) = class_name_option { format!("{} _{}", class_name.to_string(), scope_class_name) } else { format!("_{}", scope_class_name) }; let new_class_name_value = script_virt::Value::Str(script_virt::Str { source_id: "".to_string(), value: new_class_name, }); data .values .insert("class".to_string(), new_class_name_value.clone()); } let children: Vec = ret_children .into_iter() .map(|child| script_virt::Value::Node(child)) .collect(); script_children.values.extend(children); if script_children.values.len() > 0 { data.values.insert( "children".to_string(), script_virt::Value::Array(script_children), ); } Ok(script_virt::Value::Object(data)) } fn combine_attr_value( value: String, other_value: Option<&Option>, separator: String, ) -> String { if let Some(v) = other_value { if let Some(v2) = v { return format!("{}{}{}", value, separator, v2); } } return value; } fn evaluate_component_instance<'a>( instance_element: &ast::Element, render_strategy: RenderStrategy, imported: bool, depth: u32, instance_source: Option, annotations: &Option, dep_uri: &String, context: &'a mut Context, ) -> Result, RuntimeError> { if let Some(dep) = &context.graph.dependencies.get(&dep_uri.to_string()) { let data = create_component_instance_data(instance_element, depth, context)?; if let DependencyContent::Node(node) = &dep.content { let mut instance_context = create_context( &node, dep_uri, context.graph, context.vfs, &data, context.render_call_stack.clone(), context.evaluated_graph, context.include_used_exprs, context.mode, context.id_generator.new_id(), ); check_instance_loop(&render_strategy, instance_element, &mut instance_context)?; // TODO: if fragment, then wrap in span. If not, then copy these attributes to root element let ret = evaluate_instance_node( &node, &mut instance_context, render_strategy, imported, depth, annotations, instance_source.or(Some(ElementSource::new_from_element(instance_element))), ); context.used_expr_ids.extend(instance_context.used_expr_ids); ret } else { Err(RuntimeError::unknown(context.uri)) } } else { Err(RuntimeError::new( format!("Dependency {} not found", dep_uri), context.uri, &Range::nil(), )) } } fn append_attribute<'a>( attributes: &mut BTreeMap>, key: &'a str, value: String, ) { let new_value = attributes .get(key) .and_then(|existing_value_option| existing_value_option.clone()) .and_then(|existing_value| Some(format!("{} {}", existing_value, value))) .or_else(|| Some(value)); attributes.insert(key.to_string(), new_value); } fn evaluate_native_element<'a>( element: &ast::Element, depth: u32, instance_source: Option, annotations: &Option, context: &'a mut Context, ) -> Result, RuntimeError> { let mut attributes: BTreeMap> = BTreeMap::new(); let mut tag_name = ast::get_tag_name(element).to_string(); let attribute_data = evaluate_attributes(element, true, depth, context)?; for (key, value) in attribute_data.values { if value.truthy() { attributes.insert(key.to_string(), Some(value.to_string())); } else { attributes.insert(key.to_string(), None); } } append_attribute( &mut attributes, "class", format!("_{}", context.private_scope.as_str()), ); append_attribute( &mut attributes, "class", format!("_{}", context.public_scope.as_str()), ); for injected_scope in &context.injected_scopes { append_attribute(&mut attributes, "class", format!("_{}", injected_scope)); } // allow for tag name to be dynamically changed. if let Some(tag_name_attr_value_option) = attributes.get("tagName") { if let Some(tag_name_attr_value) = tag_name_attr_value_option { tag_name = tag_name_attr_value.to_string(); } attributes.remove("tagName"); } let (children, contains_style) = evaluate_children(&element.children, depth, context)?; if contains_style { // TODO - this needs to be scoped let element_scope = &element.id; append_attribute(&mut attributes, "class", format!("_{}", element_scope)); } Ok(Some(virt::Node::Element(virt::Element { id: context.id_generator.new_id(), source_info: virt::ElementSourceInfo { instance_of: if let Some(source) = &instance_source { Some(virt::ElementInstanceOfInfo { component_name: source.tag_name.to_string(), }) } else { None }, }, source_id: use_expr_id( if let Some(source) = &instance_source { &source.id } else { &element.id }, context, ), annotations: annotations.clone(), tag_name, attributes, children, }))) } fn evaluate_attributes( element: &ast::Element, is_native: bool, depth: u32, context: &mut Context, ) -> Result { let mut data: script_virt::Object = script_virt::Object::new(element.id.to_string()); let mut property_bound_attrs: Vec<&ast::PropertyBoundAttribute> = vec![]; for attr_expr in &element.attributes { let attr = &attr_expr; match attr { ast::Attribute::KeyValueAttribute(kv_attr) => { let actual_name = kv_attr.name.clone(); use_expr_id(&kv_attr.id, context); let (name, value_option) = if kv_attr.value == None { ( actual_name, script_virt::Value::Boolean(script_virt::Boolean { source_id: kv_attr.id.to_string(), value: true, }), ) } else { let value = evaluate_attribute_value( &element.tag_name, &actual_name, &kv_attr.value.as_ref().unwrap(), is_native, depth, context, )?; if !value.truthy() { continue; } ( actual_name, maybe_cast_attribute_script_value(&kv_attr.name, value, is_native, context), ) }; if name == "export" || name == "component" || name == "as" { continue; } data.values.insert(name, value_option); } ast::Attribute::PropertyBoundAttribute(kv_attr) => { assert_attr_slot_restrictions(&element.tag_name, &kv_attr.name, &kv_attr.range, context)?; property_bound_attrs.push(kv_attr); } ast::Attribute::SpreadAttribute(attr) => { assert_slot_restrictions(&attr.range, context)?; let attr_data = evaluate_js(&attr.script, depth + 1, context)?; match attr_data { script_virt::Value::Object(mut object) => { for (key, value) in object.values.drain() { data.values.insert( key.to_string(), maybe_cast_attribute_script_value(&key, value, is_native, context), ); } } _ => { return Err(RuntimeError::new( "Spread value must be an object.".to_string(), context.uri, &element.range, )); } }; } ast::Attribute::ShorthandAttribute(sh_attr) => { use_expr_id(&sh_attr.id, context); let name = sh_attr.get_name().map_err(|message| RuntimeError { uri: context.uri.to_string(), message: message.to_string(), range: Range::nil(), })?; assert_attr_slot_restrictions(&element.tag_name, &name, &sh_attr.range, context)?; let mut script_value = evaluate_attribute_slot(&sh_attr.reference, depth, context)?; if script_value.truthy() { script_value = maybe_cast_attribute_script_value(&name, script_value, true, context); data.values.insert(name.to_string(), script_value); } } }; } if property_bound_attrs.len() > 0 { for kv_attr in property_bound_attrs { match context.data { script_virt::Value::Object(object) => { let value_option = object.values.get(&kv_attr.binding_name); if let Some(prop_value) = value_option { if prop_value.truthy() { use_expr_id(&kv_attr.id, context); let value = if let Some(kv_value) = &kv_attr.value { evaluate_attribute_value( &element.tag_name, &kv_attr.name, &kv_value, is_native, depth, context, )? } else { evaluate_attribute_key_value_string( &kv_attr.name, &kv_attr.binding_name, &kv_attr.id, &kv_attr.range, is_native, context, )? }; let combined_value = if let Some(existing_value) = data.values.get(&kv_attr.name) { script_virt::Value::Str(script_virt::Str { source_id: use_expr_id(&kv_attr.id, context), value: format!( "{} {}", value.to_string(), stringify_attribute_value(&kv_attr.name, existing_value) ), }) } else { value }; data.values.insert(kv_attr.name.to_string(), combined_value); } } } _ => {} } } } Ok(data) } fn stringify_attribute_value(key: &String, value: &script_virt::Value) -> String { if key == "style" { if let script_virt::Value::Object(object) = value { let mut buffer = String::new(); for (key, value) in object.values.iter() { buffer = format!("{}{}:{};", buffer, key.to_string(), value.to_string()); } return buffer; } } return value.to_string(); } fn evaluate_import_element<'a>( element: &ast::Element, context: &'a mut Context, ) -> Result, RuntimeError> { use_expr_id(&element.id, context); for attr in &element.attributes { use_expr_id(&attr.get_id(), context); } Ok(None) } fn evaluate_style_element<'a>( element: &ast::StyleElement, context: &'a mut Context, ) -> Result, RuntimeError> { use_expr_id(&element.id, context); Ok(None) } fn evaluate_children<'a>( children_expr: &Vec, depth: u32, context: &'a mut Context, ) -> Result<(Vec, bool), RuntimeError> { let mut children: Vec = vec![]; let mut contains_style = false; let mut annotations: Option = None; for child_expr in children_expr { match child_expr { ast::Node::StyleElement(element) => { use_expr_id(&element.id, context); contains_style = true; } ast::Node::Comment(comment) => { annotations = Some(evaluate_comment(comment, depth, context)?); continue; } _ => {} } match evaluate_node(child_expr, depth + 1, None, &annotations, context)? { Some(c) => match c { virt::Node::Fragment(mut fragment) => { for child in fragment.children.drain(0..) { children.push(child); } } _ => { children.push(c); } }, None => {} } annotations = None; } if depth == 0 { if let Some(last_child) = children.last() { if let virt::Node::Text(text_node) = last_child { // remove last child if empty string if text_node.value.trim() == "" && text_node.annotations == None { children.pop(); } } } } Ok((children, contains_style)) } fn evaluate_comment<'a>( comment: &ast::Comment, depth: u32, context: &'a mut Context, ) -> Result { let mut data = script_virt::Object::new(comment.id.to_string()); for property in &comment.annotation.properties { match property { annotation_ast::AnnotationProperty::Declaration(decl) => { let value = evaluate_js(&decl.value, depth + 1, context)?; data.values.insert(decl.name.to_string(), value); } _ => { // ignore text } } } Ok(data) } fn evaluate_fragment<'a>( fragment: &ast::Fragment, depth: u32, context: &'a mut Context, ) -> Result, RuntimeError> { evaluate_children_as_fragment(&fragment.children, depth, &fragment.id, context) } fn evaluate_children_as_fragment<'a>( children: &Vec, depth: u32, source_id: &String, context: &'a mut Context, ) -> Result, RuntimeError> { let (children, _) = evaluate_children(&children, depth, context)?; Ok(Some(virt::Node::Fragment(virt::Fragment { id: context.id_generator.new_id(), source_id: use_expr_id(source_id, context), children, }))) } fn evaluate_attribute_value<'a>( tag_name: &String, name: &String, value: &ast::AttributeValue, is_native: bool, depth: u32, context: &mut Context, ) -> Result { use_expr_id(value.get_id(), context); match value { ast::AttributeValue::DyanmicString(st) => { evaluate_attribute_dynamic_string(name, st, &st.id, is_native, depth, context) } ast::AttributeValue::String(st) => { evaluate_attribute_key_value_string(name, &st.value, &st.id, &st.range, is_native, context) } ast::AttributeValue::Slot(value) => { assert_attr_slot_restrictions(tag_name, name, &value.range, context)?; let value = evaluate_attribute_slot(&value.script, depth, context)?; Ok(maybe_cast_attribute_script_value( name, value, true, context, )) } } } fn evaluate_attribute_dynamic_string<'a>( name: &String, value: &ast::AttributeDynamicStringValue, source_id: &String, is_native: bool, depth: u32, context: &mut Context, ) -> Result { add_used_expr_id(&value.id, context); let mut buffer: Vec = vec![]; let mut scope_injections: Vec = vec![]; if is_native && name == "class" { scope_injections.extend(context.document_scopes.clone()); } for part in value.values.iter() { match part { ast::AttributeDynamicStringPart::ClassNamePierce(pierce) => { if let Some(scope) = context.import_scopes.get(&pierce.class_name) { if !scope_injections.contains(scope) { scope_injections.push(scope.to_string()); } } } _ => {} } } for part in value.values.iter() { add_used_expr_id(&part.get_id(), context); buffer.push(match part { ast::AttributeDynamicStringPart::Literal(value) => { if scope_injections.len() == 0 { value.value.to_string() } else { add_scopes(&value.value, &scope_injections) } } ast::AttributeDynamicStringPart::ClassNamePierce(pierce) => { if pierce.class_name.contains(".") { let parts = pierce.class_name.split(".").collect::>(); let import_id = parts.first().unwrap().to_string(); let dep_option = context .graph .dependencies .get(context.uri) .and_then(|dep| dep.dependencies.get(&import_id)); let dep_uri = if let Some(dep_uri) = dep_option { dep_uri } else { return Err(RuntimeError::new( "Reference not found.".to_string(), context.uri, &pierce.range, )); }; // consider sm:p-3.5. Skip first ref. (BTW this is dirty AF) let class_name = &parts[1..].join(".").to_string(); let import_option = get_import(context.uri, &import_id, context); let import = if let Some(import) = import_option { import } else { return Err(RuntimeError::new( "Reference not found.".to_string(), context.uri, &pierce.range, )); }; let class_export_option = get_import_sheet(import) .class_names .get(&class_name.to_string()); let class_export = if let Some(class_export) = class_export_option { class_export } else { return Err(RuntimeError::new( "Class name not found.".to_string(), context.uri, &pierce.range, )); }; if class_export.public { format!( "_{}_{} {}", get_document_style_public_scope(dep_uri), class_name, class_name ) } else { return Err(RuntimeError::new( "This class reference is private.".to_string(), context.uri, &pierce.range, )); } } else { if let Some(scope) = context.import_scopes.get(&pierce.class_name) { format!("_{}", scope) } else { format!( "_{}_{} _{}_{} {}", context.private_scope, pierce.class_name, context.public_scope, pierce.class_name, pierce.class_name ) } } } ast::AttributeDynamicStringPart::Slot(statement) => { evaluate_attribute_slot(statement, depth, context) .unwrap() .to_string() } }); } let mut val = buffer.join(""); let script_value = script_virt::Value::Str(script_virt::Str { source_id: use_expr_id(&source_id, context), value: val.to_string(), }); Ok(maybe_cast_attribute_script_value( name, script_value, is_native, context, )) } fn add_scopes<'a>(class_names: &'a str, scopes: &Vec) -> String { class_names .split(" ") .map(|part| { if part.trim() == "" { return part.to_string(); } else { let mut buffer = vec![]; for scope in scopes { buffer.push(format!("_{}_{}", scope, part)); } buffer.push(part.to_string()); buffer.join(" ") } }) .collect::>() .join(" ") } fn get_import<'a>( source_uri: &String, import_id: &String, context: &'a Context, ) -> Option<&'a DependencyEvalInfo> { context .graph .dependencies .get(source_uri) .and_then(|source: &Dependency| { source .dependencies .get(import_id) .and_then(|dep_uri| context.evaluated_graph.get(dep_uri)) }) } fn get_import_sheet<'a>(ev: &'a DependencyEvalInfo) -> &'a css_export::Exports { match &ev { DependencyEvalInfo::CSS(css) => &css.exports, DependencyEvalInfo::PC(pc) => &pc.exports.style, } } fn is_component_instance<'a>(element: &ast::Element, context: &Context<'a>) -> bool { context.import_ids.contains(&ast::get_tag_name(&element)) || context.part_ids.contains(&element.tag_name) } fn transform_style_value<'a>(value: &String, context: &mut Context) -> String { // Fixes https://github.com/paperclipui/paperclip/issues/336 // Primitive but easy solution lazy_static! { static ref UNDEFINED_STYLES_RE: Regex = Regex::new(r"[^\s]+:\s*undefined;?").unwrap(); } if UNDEFINED_STYLES_RE.is_match(value) { let mut new_value = value.to_string(); for _caps in UNDEFINED_STYLES_RE.captures_iter(value.to_string().as_str()) { new_value = UNDEFINED_STYLES_RE .replace(new_value.as_str(), "") .to_string(); } // trim in case string is empty return new_value.trim().to_string(); } return value.to_string(); } fn cast_attribute_value<'a>( name: &String, value: &String, is_native: bool, context: &mut Context, ) -> Option { if !is_native { return None; } if name == "style" { Some(transform_style_value(value, context)) } else { None } } fn maybe_cast_attribute_script_value<'a>( name: &String, value: script_virt::Value, is_native: bool, context: &mut Context, ) -> script_virt::Value { let str_value = value.to_string(); if let Some(casted_value) = cast_attribute_value(name, &str_value, is_native, context) { script_virt::Value::Str(script_virt::Str { source_id: use_expr_id(value.get_source_id(), context), value: casted_value.to_string(), }) } else { value } } fn evaluate_attribute_key_value_string<'a>( name: &String, value: &String, source_id: &String, range: &Range, is_native: bool, context: &mut Context, ) -> Result { let mut val = value.clone(); if let Some(casted_value) = cast_attribute_value(name, value, is_native, context) { val = casted_value; } else if name == "src" { if is_relative_path(&value) { val = resolve_asset(&context.uri, &value, range, &context.vfs)?; } } else if name == "class" && is_native { val = add_scopes(value, &context.document_scopes); } Ok(script_virt::Value::Str(script_virt::Str { value: val.clone(), source_id: source_id.to_string(), })) } fn evaluate_attribute_slot<'a>( script: &script_ast::Expression, depth: u32, context: &'a mut Context, ) -> Result { evaluate_js(script, depth + 1, context) } fn assert_attr_slot_restrictions( _tag_name: &String, _attr_name: &String, range: &Range, context: &Context, ) -> Result<(), RuntimeError> { // if tag_name == "component" { // match attr_name.as_str() { // "component" | "export" | "as" => { // return Err(RuntimeError::new("Cannot bind to reserved attribute name.".to_string(), context.uri, location)); // }, // _ => { // } // } // } assert_slot_restrictions(range, context)?; return Ok(()); } fn assert_slot_restrictions(_range: &Range, _context: &Context) -> Result<(), RuntimeError> { // if !in_instance(context) { // return Err(RuntimeError::new( // "Bindings can only be defined within components.".to_string(), // context.uri, // location, // )); // } return Ok(()); } pub fn __test__evaluate_pc_code<'a>( code: &'a str, ) -> (Result, DependencyGraph) { let mut files: BTreeMap = BTreeMap::new(); files.insert("some-file.pc".to_string(), code.to_string()); __test__evaluate_pc_files(files, "some-file.pc") } pub fn __test__evaluate_pc_files<'a>( files: BTreeMap, main_file_name: &'a str, ) -> (Result, DependencyGraph) { let mut graph = DependencyGraph::new(); let uri = main_file_name.to_string(); let vfs = VirtualFileSystem::new( Box::new(|_| "".to_string()), Box::new(|_| true), Box::new(|_, uri| Some(uri.to_string())), ); for (file_name, content) in files { graph.dependencies.insert( file_name.clone(), Dependency::from_source( content.to_string(), &file_name, &vfs, get_document_id(&file_name).as_str(), ) .unwrap(), ); } ( evaluate( &uri, &graph, &vfs, &BTreeMap::new(), false, &EngineMode::SingleFrame, "#".to_string(), ), graph, ) } ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/export.rs ================================================ use crate::css::runtime::export as css_export; use serde::Serialize; use std::collections::{BTreeMap, HashMap}; #[derive(Debug, PartialEq, Clone, Serialize)] pub struct Exports { pub style: css_export::Exports, pub components: BTreeMap, } #[derive(Debug, PartialEq, Clone, Serialize)] pub struct ComponentExport { pub name: String, pub properties: BTreeMap, pub public: bool, } #[derive(Debug, PartialEq, Clone, Serialize)] pub struct Property { pub name: String, pub optional: bool, } ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/inspect_node_styles.rs ================================================ /* TODO: - media match - compute selector scopes - global, or from specific doc - scan styles from other files */ use super::evaluator::EvalInfo as PCEvalInfo; use super::evaluator::{evaluate as evaluate_pc, EngineMode, __test__evaluate_pc_code}; use super::inspect_selector_info as iso; use super::selector_match::{ element_matches_selector_text_edge, get_selector_text_matching_sub_selector, }; use crate::core::eval::DependencyEvalInfo; use crate::core::graph::DependencyGraph; use crate::css::ast as css_ast; use crate::css::declaration_value_ast; use crate::css::declaration_value_parser; use crate::css::runtime::media_match::media_matches; use crate::css::runtime::specificity::get_selector_text_specificity; use crate::css::runtime::virt::{CSSStyleProperty, Rule, StyleRule}; use crate::engine::engine::__test__evaluate_pc_files; use crate::pc::runtime::virt as pc_virt; use crate::script::runtime::virt as script_virt; use cached::proc_macro::cached; use cached::SizedCache; use serde::Serialize; use std::collections::{BTreeMap, HashSet}; /* inherited props: border-collapse border-spacing caption-side color cursor direction empty-cells font-family font-size font-style font-variant font-weight font-size-adjust font-stretch font letter-spacing line-height list-style-image list-style-position list-style-type list-style orphans quotes tab-size text-align text-align-last text-decoration-color text-indent text-justify text-shadow text-transform visibility white-space widows word-break word-spacing word-wrap */ #[derive(Debug, PartialEq, Serialize, Clone)] pub struct StyleDeclarationInfo { #[serde(rename = "sourceId")] pub source_id: String, pub name: String, pub value: Option, #[serde(rename = "rawValue")] pub raw_value: String, // true / false if being used. Needs to be here in case of !important flag pub active: bool, } impl StyleDeclarationInfo { pub fn new(source: &CSSStyleProperty) -> StyleDeclarationInfo { StyleDeclarationInfo { source_id: source.source_id.to_string(), name: source.name.to_string(), raw_value: source.value.to_string(), value: declaration_value_parser::parse(source.value.as_str(), "").map(|value| { Some(value) }).unwrap_or(None), active: true, } } pub fn important(&self) -> bool { if let Some(value) = &self.value { value.important } else { false } } pub fn overrides(&mut self, other: &mut StyleDeclarationInfo) { if self.inherits() { return; } if other.important() && !self.important() { self.active = false; } else { other.active = false; } } pub fn cascades(&self) -> bool { let inherited_prop_names = [ "border-collapse", "border-spacing", "caption-side", "color", "cursor", "direction", "empty-cells", "font-family", "font-size", "font-style", "font-variant", "font-weight", "font-size-adjust", "font-stretch", "font", "letter-spacing", "line-height", "list-style-image", "list-style-position", "list-style-type", "list-style", "orphans", "quotes", "tab-size", "text-align", "text-align-last", "text-decoration-color", "text-indent", "text-justify", "text-shadow", "text-transform", "visibility", "white-space", "widows", "word-break", "word-spacing", "word-wrap", ]; inherited_prop_names.contains(&self.name.as_str()) || self.name.starts_with("--") } pub fn inherits(&self) -> bool { self.raw_value == "inherit" } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct MediaInfo { #[serde(rename = "conditionText")] pub condition_text: String, pub active: bool, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct StyleRuleInfo { pub inherited: bool, #[serde(rename = "selectorText")] pub selector_text: String, // info about the selector including scope stuff #[serde(rename = "selectorInfo")] pub selector_info: iso::Selector, // before, after #[serde(rename = "pseudoElementName")] pub pseudo_element_name: Option, // AST source information #[serde(rename = "sourceId")] pub source_id: String, // keep this here because it'll likely be displayed to the user. #[serde(rename = "sourceUri")] pub source_uri: String, pub media: Option, declarations: Vec, // need to do docs for this pub specificity: i32, } impl StyleRuleInfo { pub fn new( rule: &StyleRule, source_uri: &String, media: Option, matching_sub_selector: &css_ast::Selector, selector_info: iso::Selector, ) -> StyleRuleInfo { let mut rule_info = StyleRuleInfo { inherited: rule.selector_text == ":root", selector_text: rule.selector_text.to_string(), selector_info, source_uri: source_uri.to_string(), media: media.clone(), source_id: rule.source_id.clone(), declarations: vec![], pseudo_element_name: matching_sub_selector.get_pseudo_element_name(), specificity: get_selector_text_specificity(&matching_sub_selector), }; for declaration in &rule.style { rule_info.push_declaration(StyleDeclarationInfo::new(declaration)); } return rule_info; } pub fn as_inherited(&self) -> StyleRuleInfo { let mut clone = self.clone(); clone.inherited = true; for decl in &mut clone.declarations { if !decl.cascades() { decl.active = false; } } clone } pub fn push_declaration(&mut self, mut declaration: StyleDeclarationInfo) { if let Some(existing_decl) = self.get_declaration(&declaration.name) { declaration.overrides(existing_decl); } self.declarations.push(declaration); } pub fn get_declaration(&mut self, name: &String) -> Option<&mut StyleDeclarationInfo> { for existing_decl in &mut self.declarations { if &existing_decl.name == name { return Some(existing_decl); } } return None; } pub fn overrides(&mut self, other: &mut StyleRuleInfo) { if self.pseudo_element_name != other.pseudo_element_name { return; } for other_decl in &mut other.declarations { if let Some(self_decl) = self.get_declaration(&other_decl.name) { self_decl.overrides(other_decl); } } } pub fn media_active(&self) -> bool { if let Some(media) = &self.media { return media.active; } return true; } pub fn contains_cascading_declarations(&self) -> bool { for decl in &self.declarations { if decl.cascades() { return true; } } return false; } pub fn contains_declaration(&self, name: &String) -> bool { self.declarations.iter().any(|decl| &decl.name == name) } pub fn can_inherit_from_style_rule(&self, other: &StyleRuleInfo) -> bool { for declaration in &self.declarations { if declaration.inherits() && other.contains_declaration(&declaration.name) { return true; } } return false; } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct NodeInspectionInfo { #[serde(rename = "styleRules")] pub style_rules: Vec, } impl NodeInspectionInfo { pub fn new() -> NodeInspectionInfo { NodeInspectionInfo { style_rules: vec![], } } pub fn insert_style_rule(&mut self, mut rule: StyleRuleInfo) { for existing_rule in &mut self.style_rules { if existing_rule.source_id == rule.source_id { return; } } let mut insert_index: usize = 0; // first find the right index for (i, existing_rule) in &mut self.style_rules.iter().enumerate() { // always higher priority for styles applied to element if !rule.inherited && existing_rule.inherited { break; } // higher priority = lower index if rule.specificity >= existing_rule.specificity && rule.inherited == existing_rule.inherited { break; } // keep climbing until we have a higher specificity or same insert_index = i + 1; } // starting at the insertion index, override all existing declarations if rule.media_active() { for i in 0..insert_index { let existing_rule = self.style_rules.get_mut(i).unwrap(); existing_rule.overrides(&mut rule); } for i in insert_index..self.style_rules.len() { let existing_rule = self.style_rules.get_mut(i).unwrap(); rule.overrides(existing_rule); } } self.style_rules.insert(insert_index, rule); } // pub fn insert_inherited_style_rule(&mut self, rule: &StyleRuleInfo) { // // skip dupes // for style_rule in &mut self.style_rules { // if style_rule.source_id == rule.source_id { // return; // } // } // let mut new_rule = rule.as_inherited(); // for style_rule in &mut self.style_rules { // style_rule.overrides(&mut new_rule); // } // self.style_rules.push(new_rule); // } pub fn can_inherit_from_style_rule(&self, other_rule: &StyleRuleInfo) -> bool { if other_rule.contains_cascading_declarations() { return true; } for rule in &self.style_rules { if rule.can_inherit_from_style_rule(other_rule) { return true; } } return false; } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct InspectionOptions { pub screen_width: Option, pub include_inherited: bool, } pub fn inspect_node_styles( element_path: &Vec, document_uri: &String, all_eval_info: &BTreeMap, graph: &DependencyGraph, options: &InspectionOptions, ) -> NodeInspectionInfo { let mut options = options.clone(); options.screen_width = get_node_frame_width(element_path, document_uri, all_eval_info).or(options.screen_width); let mut inspection_info = inspect_local_node_styles(element_path, document_uri, all_eval_info, graph, &options); if options.include_inherited { add_inherited_properties( &mut inspection_info, element_path, document_uri, all_eval_info, graph, &options, ); } inspection_info } pub fn inspect_local_node_styles( element_path: &Vec, document_uri: &String, all_eval_info: &BTreeMap, graph: &DependencyGraph, options: &InspectionOptions, ) -> NodeInspectionInfo { let mut inspection_info = NodeInspectionInfo::new(); if let Some(main_eval_info) = get_pc_info(document_uri, all_eval_info) { // need to start with imported CSS because document styles will override these for dep_uri in &main_eval_info.all_imported_sheet_uris { add_inspection_info( &mut inspection_info, element_path, main_eval_info, dep_uri, all_eval_info, graph, options, ); } add_inspection_info( &mut inspection_info, element_path, main_eval_info, document_uri, all_eval_info, graph, options, ); } inspection_info } fn get_node_frame_width( element_path: &Vec, document_uri: &String, all_eval_info: &BTreeMap, ) -> Option { if let Some(main_eval_info) = get_pc_info(document_uri, all_eval_info) { if let Some(frame_index) = element_path.get(0) { get_frame_width(*frame_index, main_eval_info) } else { None } } else { None } } fn get_frame_width(index: usize, eval_info: &PCEvalInfo) -> Option { eval_info .preview .get_children() .and_then(|children| children.get(index)) .and_then(|node| { if let pc_virt::Node::Element(element) = node { element.get_annotation_property_value("frame", "width") } else { None } }) .and_then(|width| { if let script_virt::Value::Number(number) = width { Some(number.value as u32) } else { None } }) } fn add_inspection_info( inspection_info: &mut NodeInspectionInfo, element_path: &Vec, main_eval_info: &PCEvalInfo, uri: &String, all_eval_info: &BTreeMap, graph: &DependencyGraph, options: &InspectionOptions, ) { if let Some(pc_virt::Node::Element(element)) = &main_eval_info.preview.get_descendent(element_path) { if let Some(info) = all_eval_info.get(uri) { let style_rules = get_eval_info_selectors(info, graph, options); for (style_rule, media_option) in style_rules { // if !element_matches_selector_text_edge(&style_rule.selector_text, element) || true { // continue; // } // TODO - matches should return some result instead of boolean if let Some((matching_sub_selector, entire_selector)) = get_selector_text_matching_sub_selector( &style_rule.selector_text, element_path, &main_eval_info.preview, ) { if let Ok(selector_info) = iso::Selector::from_ast(&entire_selector, graph) { let rule = StyleRuleInfo::new( style_rule, uri, media_option.clone(), &matching_sub_selector, selector_info, ); inspection_info.insert_style_rule(rule); } } } } } } fn add_inherited_properties( inspection_info: &mut NodeInspectionInfo, element_path: &Vec, document_uri: &String, all_eval_info: &BTreeMap, graph: &DependencyGraph, options: &InspectionOptions, ) { let mut cpath = element_path.clone(); for _i in 0..(element_path.len() - 1) { // easier than slice, just as effective cpath.pop(); let ancestor_inspecto_info = inspect_local_node_styles(&cpath, document_uri, all_eval_info, graph, options); for style_rule in &ancestor_inspecto_info.style_rules { if inspection_info.can_inherit_from_style_rule(style_rule) { inspection_info.insert_style_rule(style_rule.as_inherited()); } } } } fn get_pc_info<'a>( uri: &String, all_eval_info: &'a BTreeMap, ) -> Option<&'a PCEvalInfo> { all_eval_info.get(uri).and_then(|info| match info { DependencyEvalInfo::PC(pc) => Some(pc), _ => None, }) } fn get_eval_info_selectors<'a>( eval_info: &'a DependencyEvalInfo, graph: &'a DependencyGraph, options: &'a InspectionOptions, ) -> Vec<(&'a StyleRule, Option)> { let mut style_rules: Vec<(&'a StyleRule, Option)> = vec![]; match eval_info { DependencyEvalInfo::PC(pc) => { collect_style_rules(&mut style_rules, &pc.sheet.rules, None, options); } DependencyEvalInfo::CSS(css) => { collect_style_rules(&mut style_rules, &css.sheet.rules, None, options); } } style_rules } fn collect_style_rules<'a, 'b>( style_rules: &'b mut Vec<(&'a StyleRule, Option)>, rules: &'a Vec, media: Option, options: &InspectionOptions, ) { for rule in rules { match rule { Rule::Style(style) => { style_rules.push((&style, media.clone())); } Rule::Media(media) => { collect_style_rules( style_rules, &media.rules, Some(MediaInfo { condition_text: media.condition_text.trim().to_string(), // TODO - need to set this active: options .screen_width .and_then(|screen_width| Some(media_matches(&media.condition_text, screen_width))) .unwrap_or(false), }), options, ); } _ => {} } } } #[cfg(test)] mod tests { use super::super::super::parser::*; use super::*; // #[test] fn adds_inherited_props() { let source = r#"
    "#; test_pc_code( source, vec![0, 0], InspectionOptions { screen_width: None, include_inherited: true, }, NodeInspectionInfo { style_rules: vec![], }, ); } fn test_pc_code<'a>( source: &'a str, node_path: Vec, options: InspectionOptions, expected_info: NodeInspectionInfo, ) { let (eval_info, graph) = __test__evaluate_pc_code(source); let mut eval_info_map: BTreeMap = BTreeMap::new(); eval_info_map.insert( "entry.pc".to_string(), DependencyEvalInfo::PC(eval_info.unwrap()), ); let info = inspect_node_styles( &node_path, &"entry.pc".to_string(), &eval_info_map, &graph, &options, ); assert_eq!(info, expected_info); } fn test_files<'a>( files: BTreeMap, node_path: Vec, options: InspectionOptions, expected_info: NodeInspectionInfo, ) { let (eval_info, graph) = __test__evaluate_pc_files(files, "entry.pc"); let info = inspect_node_styles( &node_path, &"entry.pc".to_string(), &eval_info, &graph, &options, ); assert_eq!(info, expected_info); } } ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/inspect_selector_info.rs ================================================ use crate::core::graph::{DependencyGraph, DependencyObject}; use crate::css::ast as css_ast; use regex::Regex; use serde::Serialize; #[derive(Debug, PartialEq, Serialize, Clone)] pub struct SelectorScopeInfo { pub id: String, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "kind")] pub enum SelectorScope { Element(SelectorScopeInfo), Document(SelectorScopeInfo), } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "kind")] pub enum Selector { List(GroupSelector), // a Element(TargetSelector), // * All(TargetSelector), // a:before PseudoElement(TargetSelector), // a:nth-child(2n) PseudoParamElement(TargetSelector), // [a] Attribute(TargetSelector), Not(WrapperSelector), // #a Id(TargetSelector), // .a Class(ClassSelector), // a[b] Combo(GroupSelector), // a > b Child(BinarySelector), // a b Descendent(BinarySelector), // a + b Adjacent(BinarySelector), // a ~ b Sibling(BinarySelector), } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct GroupSelector { pub selectors: Vec, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct BinarySelector { pub left: Box, pub right: Box, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct WrapperSelector { pub selector: Box, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct TargetSelector { pub value: String, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ClassSelector { pub name: Option, pub value: String, pub scope: Option, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Error { message: String, } impl Selector { pub fn from_ast( selector: &css_ast::Selector, graph: &DependencyGraph, ) -> Result { get_selector_info(selector, graph) } } fn get_selector_info(ast: &css_ast::Selector, graph: &DependencyGraph) -> Result { match ast { css_ast::Selector::Adjacent(ast) => Ok(Selector::Adjacent(BinarySelector { left: Box::new(get_selector_info(&ast.selector, graph)?), right: Box::new(get_selector_info(&ast.next_sibling_selector, graph)?), })), css_ast::Selector::Sibling(ast) => Ok(Selector::Sibling(BinarySelector { left: Box::new(get_selector_info(&ast.selector, graph)?), right: Box::new(get_selector_info(&ast.sibling_selector, graph)?), })), css_ast::Selector::Descendent(ast) => Ok(Selector::Descendent(BinarySelector { left: Box::new(get_selector_info(&ast.ancestor, graph)?), right: Box::new(get_selector_info(&ast.descendent, graph)?), })), css_ast::Selector::Child(ast) => Ok(Selector::Child(BinarySelector { left: Box::new(get_selector_info(&ast.parent, graph)?), right: Box::new(get_selector_info(&ast.child, graph)?), })), css_ast::Selector::Combo(ast) => { let mut selectors: Vec = vec![]; for child in &ast.selectors { selectors.push(get_selector_info(child, graph)?); } Ok(Selector::Combo(GroupSelector { selectors })) } css_ast::Selector::Group(ast) => { let mut selectors: Vec = vec![]; for child in &ast.selectors { selectors.push(get_selector_info(child, graph)?); } Ok(Selector::List(GroupSelector { selectors })) } css_ast::Selector::PseudoElement(ast) => Ok(Selector::PseudoElement(TargetSelector { value: ast.to_string(), })), css_ast::Selector::PseudoParamElement(ast) => { Ok(Selector::PseudoParamElement(TargetSelector { value: ast.to_string(), })) } css_ast::Selector::Element(ast) => Ok(Selector::Element(TargetSelector { value: ast.to_string(), })), css_ast::Selector::Class(ast) => { let scope_parts = split_class_scope_parts(&ast.class_name); let scope_id = scope_parts.first().unwrap_or(&ast.class_name); let scope = graph.get_object_by_id(&scope_id).and_then(|(uri, obj)| { Some(match obj { DependencyObject::Dependency(dep) => SelectorScope::Document(SelectorScopeInfo { id: scope_id.to_string(), }), DependencyObject::Expression(dep) => SelectorScope::Element(SelectorScopeInfo { id: scope_id.to_string(), }), }) }); let cleaned_value = if let Some(class_name) = scope_parts.last() { if class_name != scope_id { Some(class_name.to_string()) } else { None } } else { Some(ast.class_name.to_string()) }; return Ok(Selector::Class(ClassSelector { name: cleaned_value, value: ast.to_string(), scope, })); } css_ast::Selector::Attribute(ast) => Ok(Selector::Attribute(TargetSelector { value: ast.to_string(), })), css_ast::Selector::AllSelector(ast) => Ok(Selector::PseudoElement(TargetSelector { value: ast.to_string(), })), css_ast::Selector::Id(ast) => Ok(Selector::Id(TargetSelector { value: ast.to_string(), })), css_ast::Selector::Not(ast) => Ok(Selector::Not(WrapperSelector { selector: Box::new(get_selector_info(&ast.selector, graph)?), })), css_ast::Selector::Prefixed(_) | css_ast::Selector::This(_) | css_ast::Selector::SubElement(_) | css_ast::Selector::Within(_) | css_ast::Selector::Global(_) => { return Err(Error { message: "invalid".to_string(), }) } } } fn split_class_scope_parts(class_selector_text: &String) -> Vec { lazy_static! { static ref scope_name_re: Regex = Regex::new(r"_(pub-)?([^_]+)(_(.+))?\b").unwrap(); } let mut scope_name_parts: Vec = vec![]; if scope_name_re.is_match(class_selector_text) { for caps in scope_name_re.captures_iter(class_selector_text) { let scope_id = caps.get(2).unwrap().as_str(); scope_name_parts.push(scope_id.to_string()); if let Some(target_name) = caps.get(4) { scope_name_parts.push(target_name.as_str().to_string()) } } } scope_name_parts } ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/inspector.rs ================================================ ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/lint.rs ================================================ /* TODO: - caniuse - a11y - /*lint-disable-next-line*/ - enforce previews* - declaration linting - noImportant */ use super::evaluator::EvalInfo; use super::evaluator::{evaluate as evaluate_pc, EngineMode, __test__evaluate_pc_code}; use super::selector_match::find_one_matching_element; use super::virt::Node as VirtNode; use crate::base::ast::{ExprSource, ExprTextSource, Range}; use serde::{Deserialize, Serialize}; // use crate::core::diagnostics::{Diagnostic, DiagnosticInfo, DiagnosticLevel, DiagnosticSourceInfo}; use crate::core::graph::DependencyGraph; use crate::css::ast as css_ast; use crate::css::runtime::virt::{ CSSStyleProperty as VirtCSSStyleProperty, Rule as VirtRule, StyleRule as VirtStyleRule, }; use crate::pc::ast as pc_ast; #[derive(Debug, PartialEq, Serialize, Clone)] pub struct LintWarningInfo { message: String, source: ExprSource, } impl LintWarningInfo { pub fn new<'a>(message: &'a str, source: &ExprSource) -> LintWarningInfo { LintWarningInfo { message: message.to_string(), source: source.clone(), } } } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "warningKind")] pub enum LintWarning { UnusedStyleRule(LintWarningInfo), } #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] pub struct LintOptions { #[serde(rename = "noUnusedStyles")] pub no_unused_styles: Option, // TODO: ["background-color", "padding", "background"]. Need to be contextually aware for cases like (background: #ab repeat) #[serde(rename = "enforceVars")] pub enforce_vars: Option>, pub enforce_previews: Option, } impl LintOptions { pub fn nil() -> LintOptions { LintOptions { no_unused_styles: None, enforce_vars: None, enforce_previews: None, } } } pub fn lint( eval_info: &EvalInfo, graph: &DependencyGraph, options: LintOptions, ) -> Vec { let mut notices: Vec = Vec::new(); lint_css(eval_info, graph, &options, &mut notices); notices } fn lint_css( eval_info: &EvalInfo, graph: &DependencyGraph, options: &LintOptions, diagnostics: &mut Vec, ) { // TODO - media rules and such lint_css_rules( &eval_info.sheet.rules, &eval_info.preview, graph, options, diagnostics, ) } fn lint_css_rules( rules: &Vec, document: &VirtNode, graph: &DependencyGraph, options: &LintOptions, diagnostics: &mut Vec, ) { // TODO - media rules and such for rule in rules { match rule { VirtRule::Style(style_rule) => { lint_style_rule(style_rule, document, graph, options, diagnostics); } VirtRule::Media(media) => lint_css_rules(&media.rules, document, graph, options, diagnostics), // TODO - look for keyframes _ => {} } } } fn lint_style_rule( style_rule: &VirtStyleRule, document: &VirtNode, graph: &DependencyGraph, options: &LintOptions, diagnostics: &mut Vec, ) { if options.no_unused_styles == Some(true) { lint_unused_style(style_rule, document, graph, options, diagnostics); } if let Some(enforce_vars) = &options.enforce_vars { for decl in &style_rule.style { lint_style_declaration(decl, graph, enforce_vars, diagnostics); } } } fn lint_style_declaration( decl: &VirtCSSStyleProperty, graph: &DependencyGraph, enforce_vars: &Vec, diagnostics: &mut Vec, ) { if !enforce_vars.contains(&decl.name) { return; } // dirty check to ensure that vars are used. This // covers most of our bases. Will need to make this more precise at // some point. if decl.value.contains("var(") || decl.value == "0" || decl.value == "0px" || decl.value.contains("transparent") || decl.value.contains("inherit") || decl.value.contains("initial") { return; } let expr_option = graph.get_expression_by_id(&decl.source_id); if let Some((uri, expr)) = expr_option { let expr_range: &Range = expr.get_range(); diagnostics.push(LintWarning::UnusedStyleRule(LintWarningInfo::new( "Variable needs to be used here", &ExprSource::new( decl.source_id.as_str(), Some(&ExprTextSource::new(uri, expr_range.clone())), ), ))); } } fn lint_unused_style( style_rule: &VirtStyleRule, document: &VirtNode, graph: &DependencyGraph, options: &LintOptions, diagnostics: &mut Vec, ) { if !style_rule.exported && find_one_matching_element(&style_rule.selector_text, document) == None { let expr_option = graph.get_expression_by_id(&style_rule.source_id); if let Some((uri, expr)) = expr_option { let mut expr_range: &Range = expr.get_range(); // check for :global if let pc_ast::Expression::CSS(cssobject) = &expr { if let css_ast::Expression::StyleRule(style) = cssobject { expr_range = style.selector.get_range(); if style.selector.is_global() { return; } } if let css_ast::Expression::Rule(rule) = cssobject { if let css_ast::Rule::Style(style) = rule { expr_range = style.selector.get_range(); if style.selector.is_global() { return; } } } } diagnostics.push(LintWarning::UnusedStyleRule(LintWarningInfo::new( "Unused style rule in preview", &ExprSource::new( style_rule.source_id.as_str(), Some(&ExprTextSource::new(uri, expr_range.clone())), ), ))); } } } #[cfg(test)] mod tests { use super::super::super::parser::*; use super::*; #[test] fn generates_warning_when_simple_style_not_found() { let source = "
    "; let (eval_info, graph) = __test__evaluate_pc_code(source); let notices = lint( &eval_info.unwrap(), &graph, LintOptions { no_unused_styles: Some(true), enforce_vars: None, enforce_previews: None, }, ); assert_eq!(notices.len(), 1); } #[test] fn generates_warnings_for_various_cases() { let cases = [ ( "
    ", 1, ), // ignore exports ("", 0), // ignore globals ( "", 0, ), // warning for magic colors ( "", 2, ), ( "", 0, ), ("", 0), ("", 0), ("", 0), ("", 0), ]; for (source, warning_count) in cases.iter() { let (eval_info, graph) = __test__evaluate_pc_code(source); let notices = lint( &eval_info.unwrap(), &graph, LintOptions { no_unused_styles: Some(true), enforce_vars: Some(vec![ "padding".to_string(), "font-family".to_string(), "color".to_string(), "background".to_string(), ]), enforce_previews: None, }, ); assert_eq!(notices.len(), *warning_count); } } } ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/mod.rs ================================================ pub mod cache; pub mod diff; pub mod evaluator; pub mod export; pub mod inspect_node_styles; pub mod inspect_selector_info; pub mod lint; pub mod mutation; pub mod selector_match; pub mod virt; ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/mutation.rs ================================================ use super::virt::{ElementSourceInfo, Node}; use crate::base::ast::ExprTextSource; use crate::css::runtime::mutation as css_mutation; use crate::script::runtime::virt as script_virt; use serde::Serialize; #[derive(Debug, PartialEq, Serialize, Clone)] pub struct InsertChild { pub child: Node, pub index: usize, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct DeleteChild { pub index: usize, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct SetAttribute { pub name: String, pub value: Option, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct RemoveAttribute { pub name: String, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct UpdateSheet { pub mutations: Vec, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct SetText { pub value: String, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct SetAnnotations { pub value: Option, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct SetElementSourceInfo { pub value: ElementSourceInfo, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct SetElementSourceId { pub value: String, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ReplaceNode { pub replacement: Node, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "kind")] pub enum Action { ReplaceNode(ReplaceNode), InsertChild(InsertChild), DeleteChild(DeleteChild), SetAttribute(SetAttribute), SetText(SetText), SetAnnotations(SetAnnotations), // DEPRECATED, should use source ID and pull AST from that SetElementSourceInfo(SetElementSourceInfo), SetElementSourceId(SetElementSourceId), RemoveAttribute(RemoveAttribute), UpdateSheet(UpdateSheet), } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Mutation { #[serde(rename = "nodePath")] node_path: Vec, action: Action, } impl Mutation { pub fn new(node_path: Vec, action: Action) -> Mutation { Mutation { node_path, action } } } ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/selector_match.rs ================================================ /* Considerations: - nested styles - exported styles (may wany to lint exports if all PC files are provided) - sibling selectors */ /* Brainstorm: a b { } traversing from the selected element all the way to the root is a bit finicky considering that we still need to traverse _down_ from the outermost selector ast. For example "a b" can be expressed as: { type: "Descendent", ancestor: { type: "tag", name: "a" }, descendent: { type: "tag", name: "b" } } If we're applying logic via `matches_element("a b", b, b_path, root)`, then we have 2 options: 1. match specific leafs and traverse _up_ to make sure the selector matches with ancestors. 2. traverse starting from ancestors - this will cause duplicate items to be added to the matched selectors (see above). Very expensive since this demands extra recursion: `a b -> ` would match b _twice_ 1 is prefered. Maybe there's a way to invert this for performance? selector.invert() could yield: { selector: { type: "tag", name: "a" }, parent: { type: "Descendent", selector: { type: "a" } } } The _good_ news is that I think there are only a few selectors that would need inverted rules: - `a b` - `a > b` Doubly nested selectors? `a > b[href] c` would yield: { selector: { type: "tag", name: "c" }, next: { type: "Ancestor", selector: { type: "Combo", selectors }, next: { type: "Parent" }, selector: {} } } Combo selectors? `a b, c > d` would yield: { type: "Combo", selectors: { type: "Inverted", } } */ use super::virt::{Element as VirtElement, Node as VirtNode}; use crate::css::ast as css_ast; use crate::css::parser::parse_selector as parse_css_selector; use cached::proc_macro::cached; use cached::SizedCache; use regex::Regex; use std::collections::HashMap; use std::sync::Mutex; #[derive(Debug)] struct Context<'a> { path: Vec<(usize, &'a VirtNode)>, } impl<'a> Context<'a> { fn new() -> Context<'a> { Context { path: Vec::new() } } fn new_from_path(node_path: &Vec, document: &'a VirtNode) -> Option> { let mut curr = Context::new(); let mut parent = document; for i in node_path { let curr_option = parent .get_children() .and_then(|children| children.get(*i)) .and_then(|child| Some(curr.child(*i, parent))); if let Some(new_curr) = curr_option { curr = new_curr; parent = curr.target().unwrap(); } else { return None; } } Some(curr) } fn child(&self, index: usize, parent: &'a VirtNode) -> Context<'a> { let mut path = self.path.clone(); path.push((index, parent)); Context { path } } fn parent_context(&self) -> Option> { if self.path.len() == 0 { return None; } let mut path = self.path.clone(); path.pop(); Some(Context { path }) } fn parent_element(&self) -> Option<&'a VirtNode> { if let Some((index, parent)) = self.path.last() { return Some(parent); } return None; } fn index(&self) -> Option { return self.path.last().and_then(|(index, _)| return Some(*index)); } fn target(&self) -> Option<&'a VirtNode> { if let Some((index, parent)) = self.path.last() { return parent .get_children() .and_then(|children| children.get(*index)); } return None; } } pub fn selector_text_matches_element<'a, 'b>( selector_text: &'b str, element_path: &'a Vec, document: &'a VirtNode, ) -> bool { get_selector_text_matching_sub_selector(selector_text, element_path, document) != None } pub fn element_matches_selector_text_edge<'a, 'b>( selector_text: &'a str, element: &'b VirtElement, ) -> bool { let selector = parse_css_selector(selector_text, None).unwrap(); let context = Context::new(); element_matches_selector_edge(&selector, element, &context) } fn element_matches_selector_edge<'a, 'b>( selector: &css_ast::Selector, element: &'b VirtElement, context: &Context, ) -> bool { match selector { css_ast::Selector::Combo(_) | css_ast::Selector::Class(_) | css_ast::Selector::Attribute(_) | css_ast::Selector::Element(_) | css_ast::Selector::AllSelector(_) | css_ast::Selector::Id(_) => get_matching_sub_selector(selector, element, context) != None, css_ast::Selector::Descendent(sel) => { element_matches_selector_edge(&sel.descendent, element, context) } css_ast::Selector::Child(sel) => element_matches_selector_edge(&sel.child, element, context), css_ast::Selector::Sibling(sel) => { element_matches_selector_edge(&sel.sibling_selector, element, context) } css_ast::Selector::Adjacent(sel) => { element_matches_selector_edge(&sel.next_sibling_selector, element, context) } css_ast::Selector::Group(sel) => sel .selectors .iter() .any(|child| element_matches_selector_edge(child, element, context)), css_ast::Selector::Not(sel) => !element_matches_selector_edge(&sel.selector, element, context), css_ast::Selector::Prefixed(_) | css_ast::Selector::Global(_) | css_ast::Selector::This(_) | css_ast::Selector::Within(_) | css_ast::Selector::SubElement(_) => false, css_ast::Selector::PseudoParamElement(_) | css_ast::Selector::PseudoElement(_) => true, } } pub fn get_selector_text_matching_sub_selector<'a, 'b>( selector_text: &'b str, element_path: &'a Vec, document: &'a VirtNode, ) -> Option<(css_ast::Selector, css_ast::Selector)> { let selector = parse_css_selector(selector_text, None).unwrap(); Context::new_from_path(element_path, document) .and_then(|context| { context .target() .and_then(|node| match node { VirtNode::Element(el) => Some(el), _ => None, }) .and_then(|element| get_matching_sub_selector(&selector, element, &context)) .and_then(|sub_selector| Some(sub_selector.clone())) }) .and_then(|sub_selector| Some((sub_selector, selector))) } // Note that we pub fn get_matching_elements<'a, 'b>( selector_text: &'b str, document: &'a VirtNode, ) -> Vec<&'a VirtElement> { // necessary to use selector text here since we're pulling from virt CSSOM, and _that_'s necessary // since we may be dealing with nested styles and such -- that information is included in the style selector, but is // lost within the CSS AST (unless parent el or scope is provided for context - that's just too cumbersome). We can // deal with the performance penalty 😭 let selector = parse_css_selector(selector_text, None).unwrap(); let mut matching_elements: Vec<&'a VirtElement> = Vec::new(); traverse_tree( &selector, document, Context::new(), &mut |child, context| { if get_matching_sub_selector(&selector, child, context) != None { matching_elements.push(child); } true }, ); return matching_elements; } pub fn find_one_matching_element<'a, 'b>( selector_text: &'b str, document: &'a VirtNode, ) -> Option<&'a VirtElement> { let selector = parse_css_selector(selector_text, None).unwrap(); let mut found: Option<&'a VirtElement> = None; traverse_tree( &selector, document, Context::new(), &mut |child, context| { if get_matching_sub_selector(&selector, child, context) != None { found = Some(child); false } else { true } }, ); return found; } fn traverse_tree<'a>( selector: &css_ast::Selector, node: &'a VirtNode, context: Context, each: &mut dyn FnMut(&'a VirtElement, &Context) -> bool, ) -> bool { if let VirtNode::Element(element) = node { if !each(element, &context) { return false; } } if let Some(children) = node.get_children() { for (i, child) in children.iter().enumerate() { if !traverse_tree(selector, child, context.child(i, node), each) { return false; } } } return true; } fn get_matching_sub_selector<'a, 'b, 'c>( selector: &'c css_ast::Selector, element: &'a VirtElement, context: &'a Context, ) -> Option<&'c css_ast::Selector> { match selector { // a, b, c css_ast::Selector::Group(sel) => { for selector in &sel.selectors { if let Some(sub_selector) = get_matching_sub_selector(&selector, element, context) { return Some(sub_selector); } } } // a[b] css_ast::Selector::Combo(sel) => { for selector in &sel.selectors { if get_matching_sub_selector(selector, element, context) == None { return None; } } return Some(&selector); } // :hover css_ast::Selector::PseudoElement(sel) => { if sel.name == "disabled" && element.get_attribute("disabled") == None { return None; } // TODO - need to have flag for states like this. For now, ignore // https://www.w3schools.com/css/css_pseudo_classes.asp if matches!( sel.name.as_str(), "hover" | "active" | "visited" | "link" | "focus" ) { return None; } return Some(&selector); } // :nth-child(2n) css_ast::Selector::PseudoParamElement(sel) => { // TODO - need to do simple parse logic return Some(&selector); } // a b c d -> [a [b [c d]]] // This is a bit tricky, but basically what's happening is that we're // starting from the right-most descendent -- that recursively looks up using corresponding // ancestor selectors along the way. Ancestor selectors are always _leaf_ selectors. css_ast::Selector::Descendent(sel) => { // _may_ be a descendent element if get_matching_sub_selector(&sel.descendent, element, context) == None { return None; } let mut ancestor_context_option = context.parent_context(); // Loop through parents until there is a match while let Some(ancestor_context) = ancestor_context_option { if let Some(ancestor_target) = ancestor_context.target() { if let VirtNode::Element(ancestor_element) = ancestor_target { // this will *always* be a leaf selector if get_matching_sub_selector(&sel.ancestor, ancestor_element, &ancestor_context) != None { return Some(&selector); } } } else { break; } ancestor_context_option = ancestor_context.parent_context(); } return None; } // :has css_ast::Selector::SubElement(sel) => { return context.target().and_then(|self_context| { if selector_matches_nested_element(&sel.selector, self_context, &context) { return Some(selector); } return None; }); } // :global(a) css_ast::Selector::Global(_) | css_ast::Selector::Prefixed(_) | css_ast::Selector::This(_) | css_ast::Selector::Within(_) => { panic!("Unsupported selector for match."); } // :not(a) css_ast::Selector::Not(not) => { if get_matching_sub_selector(¬.selector, element, context) == None { return Some(¬.selector); } return None; } // a > b css_ast::Selector::Child(sel) => { if get_matching_sub_selector(&sel.child, element, context) == None { return None; } return context .parent_context() .and_then(|parent_context| { return parent_context.target().and_then(|parent_node| { if let VirtNode::Element(parent_element) = parent_node { get_matching_sub_selector(&sel.parent, &parent_element, &parent_context) } else { None } }); }) .and_then(|_| Some(selector)); } // a + b css_ast::Selector::Adjacent(sel) => { if get_matching_sub_selector(&sel.next_sibling_selector, element, context) == None { return None; } return context .parent_element() .and_then(|parent| parent.get_children()) .and_then(|children| { let index = context.index().unwrap(); // ff is totally valid - just find next adjascent element for i in (0..index).rev() { let sibling = children.get(i); if let Some(node) = sibling { if let VirtNode::Element(element) = node { if get_matching_sub_selector( &sel.selector, &element, &context.parent_context().unwrap().child(i, node), ) != None { return Some(sel.next_sibling_selector.as_ref()); } return None; } } } None }); } // a ~ b css_ast::Selector::Sibling(sel) => { if get_matching_sub_selector(&sel.sibling_selector, element, context) == None { return None; } return context .parent_element() .and_then(|parent| parent.get_children()) .and_then(|children| { let index = context.index().unwrap(); // ff is totally valid - just find next adjascent element for i in (0..index).rev() { let sibling = children.get(i); if let Some(node) = sibling { if let VirtNode::Element(element) = node { if get_matching_sub_selector( &sel.selector, &element, &context.parent_context().unwrap().child(i, node), ) != None { return Some(sel.sibling_selector.as_ref()); } } } } None }); } // #id css_ast::Selector::Id(sel) => { return element .get_attribute("id") .and_then(|id_attr| { return id_attr; }) .and_then(|id| if id == sel.id { Some(selector) } else { None }); } // .class css_ast::Selector::Class(sel) => { lazy_static! { static ref ESCAPE_RE: Regex = Regex::new(r"\\").unwrap(); static ref CACHE: Mutex>> = Mutex::new(HashMap::new()); } return element .get_attribute("class") .and_then(|value_option| value_option) .and_then(|classes| { let mut class_name_cache = CACHE.lock().unwrap(); let match_class_name = ESCAPE_RE.replace_all(&sel.class_name, "").to_string(); let class_list = if let Some(class_list) = class_name_cache.get(&classes) { class_list } else { class_name_cache.insert( classes.to_string(), classes .split(" ") .map(|v| v.to_string()) .collect::>(), ); class_name_cache.get(&classes).unwrap() }; if class_list.contains(&match_class_name) { Some(selector) } else { None } }); } // * css_ast::Selector::AllSelector(_) => { return Some(selector); } css_ast::Selector::Element(sel) => { if sel.tag_name == element.tag_name { return Some(&selector); } } css_ast::Selector::Attribute(sel) => { if let Some(operator) = &sel.operator { return element .get_attribute(&sel.name) .and_then(|value_option| value_option) .and_then(|value| { if let Some(attr_value) = &sel.value { Some((attr_value, value)) } else { None } }) .and_then(|(attr_value, value)| { match operator.as_str() { "=" => { if attr_value == &value || attr_value == &format!("\"{}\"", value) || attr_value == &format!("'{}'", value) { return Some(selector); } } _ => {} } None }); } else { if element.get_attribute(&sel.name) != None { return Some(&selector); } } } } None } #[cached( type = "SizedCache", create = "{ SizedCache::with_size(100) }", convert = r#"{ format!("{}{}", class, value) }"# )] fn class_contains(class: &str, value: &str) -> bool { false } fn selector_matches_nested_element<'a>( selector: &css_ast::Selector, parent: &'a VirtNode, context: &'a Context, ) -> bool { if let Some(children) = parent.get_children() { for (i, child) in children.iter().enumerate() { if let VirtNode::Element(child_element) = child { if get_matching_sub_selector(selector, child_element, &context.child(i, parent)) != None { return true; } } } } return false; } #[cfg(test)] mod tests { use super::super::evaluator::{evaluate as evaluate_pc, EngineMode, __test__evaluate_pc_code}; use super::*; // ID #[test] fn can_match_tag_name_selector() { let selector_source = "div"; let pc_source = "
    "; let (eval_info, _) = __test__evaluate_pc_code(pc_source); let info2 = &eval_info.unwrap(); let elements = get_matching_elements(&selector_source, &info2.preview); assert_eq!(elements.len(), 1); } #[test] fn can_match_attr() { let selector_source = "[href]"; let pc_source = " "; let (eval_info, _) = __test__evaluate_pc_code(pc_source); let info2 = &eval_info.unwrap(); let elements = get_matching_elements(&selector_source, &info2.preview); assert_eq!(elements.len(), 1); } #[test] fn can_match_attr_with_value() { let selector_source = "[href=#]"; let pc_source = ""; let (eval_info, _) = __test__evaluate_pc_code(pc_source); let info2 = &eval_info.unwrap(); let elements = get_matching_elements(&selector_source, &info2.preview); assert_eq!(elements.len(), 1); } #[test] fn can_match_id_selector() { let selector_source = "#a"; let pc_source = "
    "; let (eval_info, _) = __test__evaluate_pc_code(pc_source); let info2 = &eval_info.unwrap(); let elements = get_matching_elements(&selector_source, &info2.preview); assert_eq!(elements.len(), 1); } #[test] fn can_match_adj_selector() { let selector_source = "a + b"; let pc_source = ""; let (eval_info, _) = __test__evaluate_pc_code(pc_source); let info2 = &eval_info.unwrap(); let elements = get_matching_elements(&selector_source, &info2.preview); assert_eq!(elements.len(), 1); } #[test] fn can_match_nested_adj_selector() { let selector_source = "a + b"; let pc_source = "
    "; let (eval_info, _) = __test__evaluate_pc_code(pc_source); let info2 = &eval_info.unwrap(); let elements = get_matching_elements(&selector_source, &info2.preview); assert_eq!(elements.len(), 1); } #[test] fn can_match_sib_selector() { let selector_source = "a ~ b"; let pc_source = "
    "; let (eval_info, _) = __test__evaluate_pc_code(pc_source); let info2 = &eval_info.unwrap(); let elements = get_matching_elements(&selector_source, &info2.preview); assert_eq!(elements.len(), 1); } // ANCESTORS #[test] fn can_match_ancestor_selector() { let selector_source = "[href=#]"; let pc_source = ""; let (eval_info, _) = __test__evaluate_pc_code(pc_source); let info2 = &eval_info.unwrap(); let elements = get_matching_elements(&selector_source, &info2.preview); assert_eq!(elements.len(), 1); } #[test] fn can_match_various_selectors() { let cases = [ // element selectors ("div", "
    ", 1), (".div", "
    ", 1), ("#div", "
    ", 1), ("[href=#]", "", 1), ("a[href]", "
    ", 2), ("a[href='#']", "
    ", 1), ("a:before", "", 1), ("div:nth-child(2)", "
    ", 4), (":disabled", "", 0), (":disabled", "", 1), (":not(:disabled)", "", 1), ( "div:not(.a)", "
    ", 1, ), ("a b c", "", 1), ("a c", "", 2), ("a > c", "", 1), ( ".a, .b", "
    ", 3, ), ( "*", "
    ", 3, ), ]; for (selector, html, count) in cases.iter() { let (result, _) = __test__evaluate_pc_code(html); let eval_info = &result.unwrap(); let elements = get_matching_elements(&selector, &eval_info.preview); assert_eq!(elements.len() as i32, *count); } } } ================================================ FILE: packages/paperclip-core/native/src/pc/runtime/virt.rs ================================================ extern crate wasm_bindgen; use crate::css::runtime::virt as css_virt; use crate::script::runtime::virt as script_virt; use serde::Serialize; use std::collections::BTreeMap; #[derive(Debug, PartialEq, Serialize, Clone)] pub struct NodeSource { pub path: Vec, #[serde(rename = "documentUri")] pub document_uri: String, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Fragment { pub id: String, #[serde(rename = "sourceId")] pub source_id: String, pub children: Vec, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ElementInstanceOfInfo { #[serde(rename = "componentName")] pub component_name: String, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ElementSourceInfo { #[serde(rename = "instanceOf")] pub instance_of: Option, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Slot { pub id: String, #[serde(rename = "sourceId")] pub source_id: String, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Element { pub id: String, #[serde(rename = "sourceId")] pub source_id: String, // Deprecated, use source_id instead pub annotations: Option, #[serde(rename = "tagName")] pub tag_name: String, pub attributes: BTreeMap>, pub children: Vec, #[serde(rename = "sourceInfo")] pub source_info: ElementSourceInfo, } impl Element { pub fn get_annotation(&self, name: &String) -> Option<&script_virt::Value> { self.annotations.as_ref().and_then(|annotations| { if let Some(annotation) = annotations.values.get(name) { Some(annotation) } else { None } }) } pub fn get_annotation_property_value( &self, annotation_name: &str, property_name: &str, ) -> Option<&script_virt::Value> { self .get_annotation(&annotation_name.to_string()) .and_then(|ann| { if let script_virt::Value::Object(object) = ann { object.values.get(property_name) } else { None } }) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct StyleElement { pub id: String, #[serde(rename = "sourceId")] pub source_id: String, pub sheet: css_virt::CSSSheet, } impl Element { pub fn get_attribute<'a>(&self, name: &'a str) -> Option> { for (key, value) in &self.attributes { if (key == name) { return Some(value.clone()); } } None } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Text { pub id: String, #[serde(rename = "sourceId")] pub source_id: String, pub annotations: Option, pub value: String, } #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "kind")] pub enum Node { Element(Element), Text(Text), Slot(Slot), Fragment(Fragment), StyleElement(StyleElement), } impl Node { pub fn prepend_child<'a>(&mut self, child: Node) { match self { Node::Element(ref mut element) => { element.children.insert(0, child); } Node::Fragment(ref mut element) => { element.children.insert(0, child); } _ => {} } } pub fn get_source_id(&self) -> &String { match self { Node::Element(value) => &value.source_id, Node::Text(value) => &value.source_id, Node::Slot(value) => &value.source_id, Node::Fragment(value) => &value.source_id, Node::StyleElement(value) => &value.source_id, } } pub fn get_children<'a>(&'a self) -> Option<&'a Vec> { match self { Node::Element(value) => Some(&value.children), Node::Fragment(value) => Some(&value.children), _ => None, } } pub fn get_descendent<'a>(&'a self, path: &Vec) -> Option<&'a Node> { let mut curr = Some(self); for i in path { curr = curr .and_then(|node| node.get_children()) .and_then(|children| children.get(*i)); if curr == None { break; } } curr } } ================================================ FILE: packages/paperclip-core/native/src/pc/tokenizer.rs ================================================ use crate::base::parser::ParseError; use crate::base::string_scanner::{Char, StringScanner}; use crate::base::tokenizer::{BaseTokenizer, Position}; // TODO - continuation byte // inspiration: https://gist.github.com/tommai78101/3631ed1f136b78238e85582f08bdc618 // https://stackoverflow.com/questions/7153935/how-to-convert-utf-8-stdstring-to-utf-16-stdwstring // https://github.com/servo/rust-cssparser/blob/5188d5e0b242431530fc37c764edf471a2c8242b/src/tokenizer.rs #[derive(PartialEq, Debug)] pub enum Token<'a> { // < LessThan, // - Minus, // * Star, // + Plus, // ~ Squiggle, // @ At, // > GreaterThan, // >>> Pierce, // $ Dollar, // /> SelfTagClose, // HtmlCommentOpen, // // LineCommentOpen, // --> HtmlCommentClose, // div, blay Word(&'a str), // 5, .5, 0.5 Number(&'a str), Byte(u8), Cluster(&'a [u8]), } pub struct Tokenizer<'a> { pub scanner: StringScanner<'a>, } impl<'a> Tokenizer<'a> { pub fn peek(&mut self, steps: u8) -> Result, ParseError> { let pos = self.scanner.get_pos(); let mut i = 0; let mut result = Err(ParseError::unknown()); while i < steps { result = self.next(); i += 1; } self.scanner.set_pos(&pos); result } pub fn peek_eat_whitespace(&mut self, steps: u8) -> Result, ParseError> { let pos = self.scanner.get_pos(); let mut i = 0; let mut result = Err(ParseError::unknown()); while i < steps { self.scanner.eat_whitespace(); result = self.next(); i += 1; } self.scanner.set_pos(&pos); result } pub fn next_expect(&mut self, expected_token: Token) -> Result, ParseError> { let utf16_pos = self.scanner.get_u16pos(); let token = self.next()?; if token == expected_token { return Ok(token); } else { return Err(ParseError::unexpected_token( utf16_pos.range_from(self.scanner.get_u16pos()), )); } } pub fn next(&mut self) -> Result, ParseError> { if self.is_eof() { return Err(ParseError::eof()); } let c = self .scanner .curr_byte() .or_else(|_| Err(ParseError::eof()))?; match c { b'/' => { if self.scanner.starts_with(b"//") { self.scanner.forward(2); Ok(Token::LineCommentOpen) } else if self.scanner.starts_with(b"/>") { self.scanner.forward(2); Ok(Token::SelfTagClose) } else { self.scanner.forward(1); Ok(Token::Backslash) } } b'$' => { self.scanner.forward(1); Ok(Token::Dollar) } b'>' => { if self.scanner.starts_with(b">>>") { self.scanner.forward(3); Ok(Token::Pierce) } else { self.scanner.forward(1); Ok(Token::GreaterThan) } } b'<' => { if self.scanner.starts_with(b" { if self.scanner.starts_with(b"-->") { self.scanner.forward(3); Ok(Token::HtmlCommentClose) } else { self.scanner.forward(1); Ok(Token::Minus) } } b'*' => { self.scanner.forward(1); Ok(Token::Star) } b'!' => { self.scanner.forward(1); Ok(Token::Bang) } b'+' => { self.scanner.forward(1); Ok(Token::Plus) } b'~' => { self.scanner.forward(1); Ok(Token::Squiggle) } b'@' => { self.scanner.forward(1); Ok(Token::At) } b',' => { self.scanner.forward(1); Ok(Token::Comma) } b':' => { self.scanner.forward(1); Ok(Token::Colon) } b';' => { self.scanner.forward(1); Ok(Token::Semicolon) } b'.' => { if self.scanner.starts_with(b"...") { self.scanner.forward(3); Ok(Token::Spread) } else { self.scanner.forward(1); let is_number = |c| matches!(c, b'0'..=b'9'); if !self.is_eof() && is_number(self.scanner.curr_byte().unwrap()) { let start = self.scanner.pos - 1; self.scanner.scan(is_number); Ok(Token::Number(self.scanner.since(start))) } else { Ok(Token::Dot) } } } b'{' => { self.scanner.forward(1); Ok(Token::CurlyOpen) } b'}' => { self.scanner.forward(1); Ok(Token::CurlyClose) } b'0'..=b'9' => { let start = self.scanner.pos; let is_number = |c| matches!(c, b'0'..=b'9'); self.scanner.scan(is_number); if self.scanner.starts_with(b".") { self.scanner.forward(1); self.scanner.scan(is_number); } Ok(Token::Number(self.scanner.since(start))) } b'[' => { self.scanner.forward(1); Ok(Token::SquareOpen) } b']' => { self.scanner.forward(1); Ok(Token::SquareClose) } b'(' => { self.scanner.forward(1); Ok(Token::ParenOpen) } b')' => { self.scanner.forward(1); Ok(Token::ParenClose) } b'#' => { self.scanner.forward(1); Ok(Token::Hash) } b'"' => { self.scanner.forward(1); Ok(Token::DoubleQuote) } b'\'' => { self.scanner.forward(1); Ok(Token::SingleQuote) } b'=' => { if self.scanner.starts_with(b"===") { self.scanner.forward(3); Ok(Token::TrippleEquals) } else if self.scanner.starts_with(b"==") { self.scanner.forward(2); Ok(Token::DoubleEquals) } else { self.scanner.forward(1); Ok(Token::Equals) } } b'a'..=b'z' | b'A'..=b'Z' => { Ok(Token::Word(self.scanner.search(|c| -> bool { matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9') }))) } b'\r' | b'\n' => { self .scanner .scan(|c| -> bool { matches!(c, b'\r' | b'\n') }); Ok(Token::Whitespace) } b' ' | b'\t' => { self.scanner.scan(|c| -> bool { matches!(c, b' ' | b'\t') }); Ok(Token::Whitespace) } _ => { let c = self .scanner .next_char() .or_else(|_| Err(ParseError::eof()))?; Ok(match c { Char::Byte(b) => Token::Byte(b), Char::Cluster(chars) => Token::Cluster(chars), }) } } } pub fn peek_eat_whitespace_is_eof(&mut self) -> bool { let start = self.scanner.get_pos(); self.scanner.eat_whitespace(); let eof = self.scanner.is_eof(); self.scanner.set_pos(&start); eof } pub fn new_from_scanner(scanner: StringScanner<'a>) -> Tokenizer<'a> { Tokenizer { scanner: scanner } } } impl<'a> BaseTokenizer<'a> for Tokenizer<'a> { fn is_eof(&self) -> bool { self.scanner.is_eof() } fn skip(&mut self) -> Result<(), ParseError> { self.next()?; Ok(()) } fn get_range(&self) -> &'a [u8] { self.scanner.source } fn get_pos(&self) -> usize { self.scanner.pos } } ================================================ FILE: packages/paperclip-core/native/src/script/ast.rs ================================================ use crate::base::ast::Range; use crate::core::ast::{Expr, ExprVisitor, StringLiteral}; use crate::pc::ast as pc_ast; use serde::Serialize; use std::fmt; #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "scriptKind")] pub enum Expression { Conjunction(Conjunction), Group(Group), Not(Not), Reference(Reference), Boolean(Boolean), String(StringLiteral), Number(Number), Array(Array), Object(Object), Node(Box), } impl Expr for Expression { fn walk<'a>(&'a self, visitor: &mut dyn ExprVisitor<'a>) { visitor.visit_script_expression(self); if !visitor.should_continue() { return; } // TODO - need to walk inside all of these match self { Expression::Conjunction(conj) => { conj.left.walk(visitor); if visitor.should_continue() { conj.right.walk(visitor); } } Expression::Node(node) => node.walk_inside(visitor), Expression::Group(expr) => expr.expression.walk(visitor), _ => {} }; } fn get_id<'a>(&'a self) -> &'a String { return self.get_id(); } fn wrap<'a>(&'a self) -> pc_ast::Expression<'a> { if let Expression::Node(node) = self { return node.wrap(); } return pc_ast::Expression::Script(self); } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Number { pub id: String, pub value: String, pub range: Range, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Boolean { pub id: String, pub value: bool, pub range: Range, } impl fmt::Display for Expression { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Expression::Reference(reference) => write!(f, "{}", reference.to_string()), Expression::Conjunction(expr) => write!(f, "{}", expr.to_string()), Expression::Group(expr) => write!(f, "{}", expr.to_string()), Expression::Not(expr) => write!(f, "{}", expr.to_string()), Expression::Node(node) => write!(f, "{}", node.to_string()), Expression::String(value) => write!(f, "\"{}\"", value.value.to_string()), Expression::Boolean(value) => write!(f, "{}", value.value.to_string()), Expression::Number(value) => write!(f, "{}", value.value.to_string()), Expression::Array(value) => write!(f, "{}", value.to_string()), Expression::Object(value) => value.fmt(f), } } } impl Expression { pub fn get_range(&self) -> &Range { match self { Expression::Reference(expr) => &expr.range, Expression::Conjunction(expr) => &expr.range, Expression::Group(expr) => &expr.range, Expression::Not(expr) => &expr.range, Expression::Node(expr) => expr.get_range(), Expression::String(expr) => &expr.range, Expression::Boolean(expr) => &expr.range, Expression::Number(expr) => &expr.range, Expression::Array(expr) => &expr.range, Expression::Object(expr) => &expr.range, } } pub fn get_id(&self) -> &String { match self { Expression::Reference(expr) => &expr.id, Expression::Conjunction(expr) => &expr.id, Expression::Group(expr) => &expr.id, Expression::Not(expr) => &expr.id, Expression::Node(expr) => expr.get_id(), Expression::String(expr) => &expr.id, Expression::Boolean(expr) => &expr.id, Expression::Number(expr) => &expr.id, Expression::Array(expr) => &expr.id, Expression::Object(expr) => &expr.id, } } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Group { pub id: String, pub range: Range, pub expression: Box, } impl fmt::Display for Group { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "({})", self.expression.to_string()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Not { pub id: String, pub range: Range, pub expression: Box, } impl fmt::Display for Not { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "!{}", self.expression.to_string()) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Conjunction { pub id: String, pub range: Range, pub left: Box, pub operator: ConjunctionOperatorKind, pub right: Box, } #[derive(Debug, PartialEq, Serialize, Clone)] pub enum ConjunctionOperatorKind { And, Or, } impl fmt::Display for Conjunction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let operator = match self.operator { ConjunctionOperatorKind::And => "&&", ConjunctionOperatorKind::Or => "||", }; write!( f, "{}{}{}", self.left.to_string(), operator, self.right.to_string() ) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Array { pub id: String, pub values: Vec, pub range: Range, } impl fmt::Display for Array { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let buffer: Vec = self.values.iter().map(|value| value.to_string()).collect(); write!(f, "{}", format!("[{}]", buffer.join(", "))) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Object { pub id: String, pub properties: Vec, pub range: Range, } impl fmt::Display for Object { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let buffer: Vec = self .properties .iter() .map(|value| value.to_string()) .collect(); write!(f, "{}", format!("{{{}}}", buffer.join(", "))) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Property { pub key: String, pub value: Expression, } impl fmt::Display for Property { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}", format!("{}:{}", self.key.to_string(), self.value.to_string()) ) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Reference { pub id: String, pub range: Range, pub path: Vec, } impl fmt::Display for Reference { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}", self .path .iter() .map(|part| { part.to_string() }) .collect::>() .join(".Expression") ) } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct ReferencePart { pub optional: bool, pub name: String, } impl fmt::Display for ReferencePart { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name); if self.optional { write!(f, "?"); } Ok(()) } } ================================================ FILE: packages/paperclip-core/native/src/script/mod.rs ================================================ pub mod ast; pub mod parser; pub mod runtime; pub mod tokenizer; ================================================ FILE: packages/paperclip-core/native/src/script/parser.rs ================================================ use super::ast; use super::tokenizer::{Token, Tokenizer}; use crate::base::ast::Range; use crate::base::parser::{get_buffer, ParseError}; use crate::base::string_scanner::StringScanner; use crate::core::ast as core_ast; use crate::core::id_generator::IDGenerator; use crate::pc::parser::parse_tag; use crate::pc::parser::Context as PCContext; use crate::pc::tokenizer::Tokenizer as PCTokenizer; struct Context<'a, 'b> { tokenizer: &'a mut Tokenizer<'b>, scope_id: String, id_generator: IDGenerator, } pub fn _parse<'a>(source: &'a str, scope_id: &'a str) -> Result { let mut tokenizer = Tokenizer::new_from_scanner(StringScanner::new(source)); parse_with_tokenizer(&mut tokenizer, "".to_string(), scope_id) } pub fn parse_with_tokenizer<'a, 'b, 'c>( tokenizer: &'a mut Tokenizer<'b>, id_seed: String, scope_id: &'c str, ) -> Result { let mut context = Context { tokenizer, scope_id: scope_id.to_string(), id_generator: IDGenerator::new(id_seed), }; parse_top(&mut context) } fn parse_top<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { parse_conjunction(context) } fn parse_conjunction<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { context.tokenizer.scanner.eat_whitespace(); let start = context.tokenizer.scanner.get_u16pos(); let left: ast::Expression = parse_expression(context)?; if context.tokenizer.scanner.is_eof() { return Ok(left); } let operator_option = match context.tokenizer.peek_eat_whitespace(1)? { Token::LogicalAnd => Some(ast::ConjunctionOperatorKind::And), Token::LogicalOr => Some(ast::ConjunctionOperatorKind::Or), _ => None, }; if let Some(operator) = operator_option { context.tokenizer.scanner.eat_whitespace(); context.tokenizer.next()?; let right = parse_top(context)?; Ok(ast::Expression::Conjunction(ast::Conjunction { id: context.id_generator.new_id(), left: Box::new(left), operator, right: Box::new(right), range: Range::new(start, context.tokenizer.scanner.get_u16pos()), })) } else { Ok(left) } } fn parse_expression<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { context.tokenizer.scanner.eat_whitespace(); let result = match context.tokenizer.peek(1)? { // Token::Minus - TODO - negate. Need to consider refs too Token::LessThan => parse_node(context), Token::Bang => parse_not(context), Token::DoubleQuote | Token::SingleQuote => parse_string(context), Token::Number(_) => parse_number(context), Token::Minus => parse_negative_number(context), Token::SquareOpen => parse_array(context), Token::CurlyOpen => parse_object(context), Token::ParenOpen => parse_group(context), _ => parse_word(context), }; result } fn parse_not<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let start = context.tokenizer.scanner.get_u16pos(); context.tokenizer.next()?; Ok(ast::Expression::Not(ast::Not { id: context.id_generator.new_id(), range: Range::new(start, context.tokenizer.scanner.get_u16pos()), expression: Box::new(parse_expression(context)?), })) } fn parse_node<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let seed = context.id_generator.new_seed(); let mut scanner = context.tokenizer.scanner.clone(); let mut pc_tokenizer = PCTokenizer::new_from_scanner(scanner); let mut pc_context = PCContext { scope_id: context.scope_id.to_string(), tokenizer: pc_tokenizer, id_generator: IDGenerator::new(seed.to_string()), }; let node = ast::Expression::Node(Box::new(parse_tag( &mut pc_context, vec![context.id_generator.seed.to_string()], None, )?)); context .tokenizer .scanner .set_pos(&pc_context.tokenizer.scanner.get_pos()); Ok(node) } fn parse_number<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let start = context.tokenizer.scanner.get_u16pos(); let buffer = get_number_buffer(context)?; Ok(ast::Expression::Number(ast::Number { id: context.id_generator.new_id(), value: buffer, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), })) } fn parse_negative_number<'a, 'b>( context: &mut Context<'a, 'b>, ) -> Result { let start = context.tokenizer.scanner.get_u16pos(); context.tokenizer.next_expect(Token::Minus)?; let num_buffer = get_number_buffer(context)?; Ok(ast::Expression::Number(ast::Number { id: context.id_generator.new_id(), value: format!("-{}", num_buffer), range: Range::new(start, context.tokenizer.scanner.get_u16pos()), })) } fn get_number_buffer<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let mut buffer = String::new(); while !context.tokenizer.scanner.is_eof() { match context.tokenizer.peek(1)? { Token::Number(value) => { context.tokenizer.next()?; buffer.extend(value.chars()); } _ => { break; } } } Ok(buffer) } fn parse_string<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let start_pos = context.tokenizer.scanner.get_u16pos(); let start = context.tokenizer.next()?; let value = get_buffer(context.tokenizer, |tokenizer| { Ok(tokenizer.peek(1)? != start) })? .to_string(); context.tokenizer.next_expect(start)?; Ok(ast::Expression::String(core_ast::StringLiteral { id: context.id_generator.new_id(), value, range: Range::new(start_pos, context.tokenizer.scanner.get_u16pos()), })) } fn parse_group<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let start = context.tokenizer.scanner.get_u16pos(); context.tokenizer.next()?; // eat ( let expression = parse_top(context)?; context.tokenizer.scanner.eat_whitespace(); context.tokenizer.next_expect(Token::ParenClose)?; // eat ) Ok(ast::Expression::Group(ast::Group { id: context.id_generator.new_id(), range: Range::new(start, context.tokenizer.scanner.get_u16pos()), expression: Box::new(expression), })) } fn parse_array<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let start = context.tokenizer.scanner.get_u16pos(); context.tokenizer.next_expect(Token::SquareOpen)?; let mut values = vec![]; while !context.tokenizer.scanner.is_eof() && context.tokenizer.peek_eat_whitespace(1)? != Token::SquareClose { values.push(parse_top(context)?); if context.tokenizer.peek(1)? == Token::SquareClose { break; } context.tokenizer.next_expect(Token::Comma)?; } context.tokenizer.scanner.eat_whitespace(); context.tokenizer.next_expect(Token::SquareClose)?; Ok(ast::Expression::Array(ast::Array { id: context.id_generator.new_id(), values, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), })) } fn parse_object<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let start = context.tokenizer.scanner.get_u16pos(); context.tokenizer.next_expect(Token::CurlyOpen)?; let mut properties = vec![]; while !context.tokenizer.scanner.is_eof() && context.tokenizer.peek_eat_whitespace(1)? != Token::CurlyClose { let key = parse_top(context)?; context.tokenizer.scanner.eat_whitespace(); let value = if context.tokenizer.peek(1)? == Token::Colon { let colon = context.tokenizer.next_expect(Token::Colon)?; context.tokenizer.scanner.eat_whitespace(); parse_top(context)? } else { key.clone() }; properties.push(ast::Property { key: key.to_string(), value, }); context.tokenizer.scanner.eat_whitespace(); if context.tokenizer.peek(1)? == Token::CurlyClose { break; } context.tokenizer.next_expect(Token::Comma)?; } context.tokenizer.scanner.eat_whitespace(); context.tokenizer.next_expect(Token::CurlyClose)?; Ok(ast::Expression::Object(ast::Object { id: context.id_generator.new_id(), properties, range: Range::new(start, context.tokenizer.scanner.get_u16pos()), })) } fn parse_boolean<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let pos = context.tokenizer.scanner.get_u16pos(); if let Token::Word(name) = context.tokenizer.next()? { if name == "true" || name == "false" { return Ok(ast::Expression::Boolean(ast::Boolean { id: context.id_generator.new_id(), value: name == "true", range: Range::new(pos, context.tokenizer.scanner.get_u16pos()), })); } } Err(ParseError::unexpected_token( pos.range_from(context.tokenizer.scanner.get_u16pos()), )) } fn token_matches_var_start(token: &Token) -> bool { match token { Token::Byte(b'_') | Token::Byte(b'$') => true, Token::Word(_) => true, _ => false, } } fn token_matches_var_part(token: &Token) -> bool { token_matches_var_start(token) || if let Token::Number(v) = token { true } else { false } } fn parse_reference_name<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { Ok( get_buffer(context.tokenizer, |tokenizer| { Ok(token_matches_var_part(&tokenizer.peek(1)?)) })? .to_string(), ) } fn parse_reference<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let pos = context.tokenizer.scanner.get_u16pos(); let part = parse_reference_part(context)?; let mut path = vec![part]; while !context.tokenizer.scanner.is_eof() && context.tokenizer.peek(1)? == Token::Dot { context.tokenizer.next()?; // eat . let pos = context.tokenizer.scanner.get_u16pos(); if token_matches_var_start(&context.tokenizer.peek(1)?) { path.push(parse_reference_part(context)?); } else { return Err(ParseError::unexpected_token( pos.range_from(context.tokenizer.scanner.get_u16pos()), )); } } Ok(ast::Expression::Reference(ast::Reference { id: context.id_generator.new_id(), path, range: Range::new(pos, context.tokenizer.scanner.get_u16pos()), })) } fn parse_reference_part<'a, 'b>( context: &mut Context<'a, 'b>, ) -> Result { let pos = context.tokenizer.scanner.get_u16pos(); let name = parse_reference_name(context)?; let optional = if !context.tokenizer.scanner.is_eof() && context.tokenizer.peek(1)? == Token::Byte(b'?') { context.tokenizer.next(); true } else { false }; Ok(ast::ReferencePart { name, optional }) } fn parse_word<'a, 'b>(context: &mut Context<'a, 'b>) -> Result { let pos = context.tokenizer.scanner.get_u16pos(); let token = context.tokenizer.peek(1)?; if let Token::Word(name) = token { if name == "true" || name == "false" { return parse_boolean(context); } } if token_matches_var_start(&token) { return parse_reference(context); } Err(ParseError::unexpected_token( pos.range_from(context.tokenizer.scanner.get_u16pos()), )) } #[cfg(test)] mod tests { use super::*; #[test] fn can_smoke_parse_various_statements() { let cases = vec![ "[{a:1}]", "someReference", "someR3f", "_someRef", "$$someRef", "some.nested.reference", "somethingOptional?", "maybe?.exists?", // nodes "", // strings "'string'", "\"something\"", // numbers "1", // arrays "['ab', 'cd']", "[1, 2, [1, 2, 3, 4]]", // objects, "{ a: 1, b: 2, d: {e, f, g: 5}}", "[[1, {a, g, f, g: 1}], 2]", "[]", "{}", "[ ]", "{ }", ]; for case in cases { let _ast = _parse(case, "").unwrap(); println!("{:?}", _ast); } } } ================================================ FILE: packages/paperclip-core/native/src/script/runtime/evaluator.rs ================================================ use super::super::ast; use super::virt; use crate::base::ast::Range; use crate::base::runtime::RuntimeError; use crate::core::ast as core_ast; use crate::pc::ast as pc_ast; use crate::pc::runtime::evaluator::{ evaluate_node as evaluate_pc_node, use_expr_id, Context as PCContext, }; pub fn evaluate<'a>( expr: &ast::Expression, depth: u32, context: &'a mut PCContext, ) -> Result { evaluate_expression(&expr, depth, context) } fn evaluate_expression<'a>( expression: &ast::Expression, depth: u32, context: &'a mut PCContext, ) -> Result { match expression { ast::Expression::Reference(reference) => evaluate_reference(reference, context), ast::Expression::Conjunction(conjunction) => evaluate_conjuction(conjunction, depth, context), ast::Expression::Group(group) => evaluate_group(group, depth, context), ast::Expression::Not(conjunction) => evaluate_not(conjunction, depth, context), ast::Expression::Node(node) => evaluate_node(node, depth, context), ast::Expression::String(value) => evaluate_string(&value, context), ast::Expression::Boolean(value) => evaluate_boolean(&value, context), ast::Expression::Number(value) => evaluate_number(&value, context), ast::Expression::Array(value) => evaluate_array(value, depth, context), ast::Expression::Object(value) => evaluate_object(value, depth, context), } } fn evaluate_group<'a>( group: &ast::Group, depth: u32, context: &'a mut PCContext, ) -> Result { use_expr_id(&group.id, context); evaluate_expression(&group.expression, depth, context) } fn evaluate_conjuction<'a>( conjunction: &ast::Conjunction, depth: u32, context: &'a mut PCContext, ) -> Result { use_expr_id(&conjunction.id, context); let left = evaluate_expression(&conjunction.left, depth, context)?; match conjunction.operator { ast::ConjunctionOperatorKind::And => { if !left.truthy() { match &*conjunction.right { ast::Expression::Conjunction(conj_right) => { if conj_right.operator == ast::ConjunctionOperatorKind::Or { evaluate_expression(&conj_right.right, depth, context) } else { Ok(left) } } _ => Ok(left), } } else { evaluate_expression(&conjunction.right, depth, context) } } ast::ConjunctionOperatorKind::Or => { if left.truthy() { Ok(left) } else { evaluate_expression(&conjunction.right, depth, context) } } } } fn evaluate_not<'a>( not: &ast::Not, depth: u32, context: &'a mut PCContext, ) -> Result { use_expr_id(¬.id, context); Ok(virt::Value::Boolean(virt::Boolean { source_id: not.id.to_string(), value: !evaluate_expression(¬.expression, depth, context)?.truthy(), })) } fn evaluate_node<'a>( node: &Box, depth: u32, context: &'a mut PCContext, ) -> Result { let node_option = evaluate_pc_node(node, depth, None, &None, context)?; if let Some(node) = node_option { Ok(virt::Value::Node(node)) } else { Ok(virt::Value::Undefined(virt::Undefined { source_id: node.get_id().to_string(), })) } } fn evaluate_string<'a>( value: &core_ast::StringLiteral, context: &'a mut PCContext, ) -> Result { use_expr_id(&value.id, context); Ok(virt::Value::Str(virt::Str { source_id: value.id.to_string(), value: value.value.to_string(), })) } fn evaluate_boolean<'a>( value: &ast::Boolean, context: &'a mut PCContext, ) -> Result { use_expr_id(&value.id, context); Ok(virt::Value::Boolean(virt::Boolean { source_id: value.id.to_string(), value: value.value, })) } fn evaluate_number<'a>( value: &ast::Number, context: &'a mut PCContext, ) -> Result { use_expr_id(&value.id, context); let value_result = value.value.parse::(); if let Ok(number) = value_result { Ok(virt::Value::Number(virt::Number { source_id: value.id.to_string(), value: number, })) } else { Err(RuntimeError::new( "Invalid number.".to_string(), context.uri, &value.range, )) } } fn evaluate_array<'a>( ary: &ast::Array, depth: u32, context: &'a mut PCContext, ) -> Result { let mut script_array = virt::Array::new(ary.id.to_string()); for value in &ary.values { script_array .values .push(evaluate_expression(&value, depth, context)?); } Ok(virt::Value::Array(script_array)) } fn evaluate_object<'a>( obj: &ast::Object, depth: u32, context: &'a mut PCContext, ) -> Result { let mut script_object = virt::Object::new(obj.id.to_string()); for property in &obj.properties { script_object.values.insert( property.key.to_string(), evaluate_expression(&property.value, depth, context)?, ); } Ok(virt::Value::Object(script_object)) } fn evaluate_reference<'a>( reference: &ast::Reference, context: &'a mut PCContext, ) -> Result { use_expr_id(&reference.id, context); let mut curr = Some(context.data); for part in &reference.path { if let Some(object) = &curr { curr = virt::get_virt_value_property(&object, &part.name); } else { return Err(RuntimeError { uri: context.uri.to_string(), message: "Cannot access property of undefined".to_string(), range: Range::nil(), }); } } if let Some(script_value) = curr { Ok(script_value.clone()) } else { Ok(virt::Value::Undefined(virt::Undefined { source_id: reference.id.to_string(), })) } } ================================================ FILE: packages/paperclip-core/native/src/script/runtime/mod.rs ================================================ pub mod evaluator; pub mod virt; ================================================ FILE: packages/paperclip-core/native/src/script/runtime/virt.rs ================================================ use crate::base::ast::ExprTextSource; use crate::pc::runtime::virt::Node; use serde::Serialize; use std::collections::HashMap; use std::fmt; // TODO - all values here need to extend JsClass #[allow(dead_code)] #[derive(Debug, PartialEq, Serialize, Clone)] #[serde(tag = "kind")] pub enum Value { Object(Object), Array(Array), Node(Node), Str(Str), Boolean(Boolean), Number(Number), Undefined(Undefined), } impl Value { pub fn truthy(&self) -> bool { match self { Value::Undefined(_) => false, Value::Boolean(value) => value.value == true, Value::Number(value) => value.value != 0.0, Value::Str(value) => (&value.value).len() != 0, _ => true, } } pub fn is_number(&self) -> bool { match self { Value::Number(value) => true, _ => false, } } pub fn get_source_id(&self) -> &String { match self { Value::Undefined(value) => &value.source_id, Value::Node(value) => &value.get_source_id(), Value::Boolean(value) => &value.source_id, Value::Number(value) => &value.source_id, Value::Str(value) => &value.source_id, Value::Object(value) => &value.source_id, Value::Array(value) => &value.source_id, } } } impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Value::Object(_object) => write!(f, "[Object object]"), Value::Str(value) => write!(f, "{}", value.value), Value::Boolean(value) => write!(f, "{}", if value.value { "true" } else { "false" }), Value::Node(_value) => write!(f, "[Object object]"), Value::Number(value) => write!(f, "{}", value.value), Value::Array(_value) => write!(f, "[Object object]"), Value::Undefined(_) => write!(f, "undefined"), } } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Str { #[serde(rename = "sourceId")] pub source_id: String, pub value: String, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Boolean { #[serde(rename = "sourceId")] pub source_id: String, pub value: bool, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Number { #[serde(rename = "sourceId")] pub source_id: String, pub value: f64, } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Undefined { #[serde(rename = "sourceId")] pub source_id: String, } #[derive(Debug, Serialize, Clone)] pub struct Object { #[serde(rename = "sourceId")] pub source_id: String, pub values: HashMap, } impl Object { pub fn new(source_id: String) -> Object { Object { source_id, values: HashMap::new(), } } } impl PartialEq for Object { fn eq(&self, other: &Self) -> bool { self.values == other.values } } #[derive(Debug, PartialEq, Serialize, Clone)] pub struct Array { pub source_id: String, pub values: Vec, } impl Array { pub fn new(source_id: String) -> Array { Array { source_id, values: vec![], } } } pub fn get_virt_value_property<'a, 'b>( value: &'a Value, property_name: &'b str, ) -> Option<&'a Value> { match value { Value::Object(object) => object.values.get(property_name), _ => None, } } ================================================ FILE: packages/paperclip-core/native/src/script/tokenizer.rs ================================================ use crate::base::parser::ParseError; use crate::base::string_scanner::{Char, StringScanner}; use crate::base::tokenizer::BaseTokenizer; #[derive(PartialEq, Debug)] pub enum Token<'a> { // && LogicalAnd, LogicalOr, // < LessThan, // - Minus, // ! Bang, // { CurlyOpen, // } CurlyClose, // ( ParenOpen, // ) ParenClose, // [ SquareOpen, // ] SquareClose, // " DoubleQuote, // ' SingleQuote, // Whitespace, // . Dot, // , Comma, // : Colon, // div, blay Word(&'a str), // 5, .5, 0.5 Number(&'a str), Byte(u8), Cluster(&'a [u8]), } pub struct Tokenizer<'a> { pub scanner: StringScanner<'a>, } impl<'a> Tokenizer<'a> { pub fn peek(&mut self, steps: u8) -> Result, ParseError> { let pos = self.scanner.get_pos(); let mut i = 0; let mut result = Err(ParseError::unknown()); while i < steps { result = self.next(); i += 1; } self.scanner.set_pos(&pos); result } pub fn peek_eat_whitespace(&mut self, steps: u8) -> Result, ParseError> { let pos = self.scanner.get_pos(); let mut i = 0; let mut result = Err(ParseError::unknown()); while i < steps { self.scanner.eat_whitespace(); result = self.next(); i += 1; } self.scanner.set_pos(&pos); result } pub fn next_expect(&mut self, expected_token: Token) -> Result, ParseError> { let utf16_pos = self.scanner.get_u16pos(); let token = self.next()?; if token == expected_token { return Ok(token); } else { return Err(ParseError::unexpected_token( utf16_pos.range_from(self.scanner.get_u16pos()), )); } } pub fn next(&mut self) -> Result, ParseError> { if self.is_eof() { return Err(ParseError::eof()); } let c = self .scanner .curr_byte() .or_else(|_| Err(ParseError::eof()))?; match c { b'&' => { if self.scanner.starts_with(b"&&") { self.scanner.forward(2); Ok(Token::LogicalAnd) } else { self.scanner.forward(1); Ok(Token::Byte(c)) } } b'|' => { if self.scanner.starts_with(b"||") { self.scanner.forward(2); Ok(Token::LogicalOr) } else { self.scanner.forward(1); Ok(Token::Byte(c)) } } b'<' => { self.scanner.forward(1); Ok(Token::LessThan) } b'-' => { self.scanner.forward(1); Ok(Token::Minus) } b'!' => { self.scanner.forward(1); Ok(Token::Bang) } b',' => { self.scanner.forward(1); Ok(Token::Comma) } b':' => { self.scanner.forward(1); Ok(Token::Colon) } b'.' => { self.scanner.forward(1); let is_number = |c| matches!(c, b'0'..=b'9'); if !self.is_eof() && is_number(self.scanner.curr_byte().unwrap()) { let start = self.scanner.pos - 1; self.scanner.scan(is_number); Ok(Token::Number(self.scanner.since(start))) } else { Ok(Token::Dot) } } b'{' => { self.scanner.forward(1); Ok(Token::CurlyOpen) } b'}' => { self.scanner.forward(1); Ok(Token::CurlyClose) } b'(' => { self.scanner.forward(1); Ok(Token::ParenOpen) } b')' => { self.scanner.forward(1); Ok(Token::ParenClose) } b'0'..=b'9' => { let start = self.scanner.pos; let is_number = |c| matches!(c, b'0'..=b'9'); self.scanner.scan(is_number); if self.scanner.starts_with(b".") { self.scanner.forward(1); self.scanner.scan(is_number); } Ok(Token::Number(self.scanner.since(start))) } b'[' => { self.scanner.forward(1); Ok(Token::SquareOpen) } b']' => { self.scanner.forward(1); Ok(Token::SquareClose) } b'"' => { self.scanner.forward(1); Ok(Token::DoubleQuote) } b'\'' => { self.scanner.forward(1); Ok(Token::SingleQuote) } b'a'..=b'z' | b'A'..=b'Z' => { Ok(Token::Word(self.scanner.search(|c| -> bool { matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9') }))) } b' ' | b'\t' | b'\r' | b'\n' => { self .scanner .scan(|c| -> bool { matches!(c, b' ' | b'\t' | b'\r' | b'\n') }); Ok(Token::Whitespace) } _ => { let c = self .scanner .next_char() .or_else(|_| Err(ParseError::eof()))?; Ok(match c { Char::Byte(b) => Token::Byte(b), Char::Cluster(chars) => Token::Cluster(chars), }) } } } pub fn new_from_scanner(scanner: StringScanner<'a>) -> Tokenizer<'a> { Tokenizer { scanner: scanner } } } impl<'a> BaseTokenizer<'a> for Tokenizer<'a> { fn is_eof(&self) -> bool { self.scanner.is_eof() } fn skip(&mut self) -> Result<(), ParseError> { self.next()?; Ok(()) } fn get_range(&self) -> &'a [u8] { self.scanner.source } fn get_pos(&self) -> usize { self.scanner.pos } } ================================================ FILE: packages/paperclip-core/package.json ================================================ { "name": "@paperclip-ui/core", "version": "18.5.6", "private": false, "description": "A hybrid approach to design & code", "main": "./index.js", "browser": "./browser.js", "typings": "index.d.ts", "keywords": [ "react", "css", "styling", "html", "designer", "visual development", "live", "realtime" ], "publishConfig": { "access": "public" }, "scripts": { "test": "npm run test:node && cd native && cargo test", "lint": "eslint src", "test:node": "mocha ./lib/test/**/*-test.js", "test:watch": "mocha ./lib/test --watch & (cd native && cargo watch -x test)", "build": "npm run build:wasm:node && npm run build:node && npm run build:browser", "build:watch": "tsc --watch & (cd native && cargo watch -w src -s 'wasm-pack build --release --target nodejs --out-dir node')", "build:node": "tsc", "build:native": "cd native && cargo build --release", "build:browser": "npm run build:esm && npm run build:wasm:browser && find ./native -name package.json -exec rm {} \\; && find ./native -name .gitignore -exec rm {} \\;", "build:esm": "tsc --build tsconfig.esm.json", "build:wasm:bundler": "cd native && wasm-pack build --release --target bundler --out-dir bundler", "build:wasm:browser": "cd native && wasm-pack build --release --target browser --out-dir browser", "build:wasm:web": "cd native && wasm-pack build --release --target web --out-dir web && cat web/paperclip.js | sed 's/import.meta.url/_NOOP_/g' > web/paperclip-no-import-meta.js && cd .. && mkdir -p dist && cp native/web/paperclip_bg.wasm dist/paperclip_bg.wasm", "build:wasm:node": "cd native && wasm-pack build --release --target nodejs --out-dir node" }, "author": "", "license": "GPLv3", "dependencies": { "@paperclip-ui/utils": "^18.5.6", "@types/mocha": "^5.2.7", "chai": "^4.2.0", "crc32": "^0.2.2", "mocha": "^7.0.1", "url": "^0.11.0" }, "gitHead": "51fbf39fb8197bd0012f96ef75fa33b82949600d", "devDependencies": { "@types/node": "^14.0.20", "@typescript-eslint/eslint-plugin": "^3.6.1", "@typescript-eslint/parser": "^3.6.1", "eslint": "^7.4.0" } } ================================================ FILE: packages/paperclip-core/pkg/index.js ================================================ ================================================ FILE: packages/paperclip-core/src/core/delegate.ts ================================================ // 🙈 import { EngineDelegateEvent, updateAllLoadedData, EngineDelegateEventKind, DependencyContent, SheetInfo, LoadedData, EvaluatedDataKind, DiffedPCData, getImportById, getImportBySrc, DependencyNodeContent, getAttributeStringValue, hasAttribute, Diagnostic, INJECT_STYLES_TAG_NAME, CoverageReport, NodeStyleInspection, RootExpression, VirtNodeSource, Dependency, Module, DiffedEvent, LoadedPCData, ExprTextSource, } from "@paperclip-ui/utils"; import { noop } from "./utils"; export type FileContent = { [identifier: string]: string; }; export type VirtualNodeSourceInfo = { sourceId: string; textSource: ExprTextSource; }; export type ErrorResult = { error: any }; export const isErrorResult = (data: any): data is ErrorResult => { return data.error != null; }; export type EngineIO = { resolveFile?: (fromPath: string, toPath: string) => string; fileExists?: (filePath: string) => boolean; readFile?: (filePath: string) => string; }; export interface IEngineDelegate { onEvent: (listener: EngineDelegateEventListener) => () => void; parseFile: (uri: string) => any; } export enum EngineMode { SingleFrame, MultiFrame, } export type EngineOptions = { io?: EngineIO; mode?: EngineMode; }; const mapResult = (result: any) => { if (!result) { return result; } if (result.Ok) { return result.Ok; } else { return { error: result.Err }; } }; export type EngineDelegateEventListener = (event: EngineDelegateEvent) => void; export enum EngineDelegateEventType { Loaded = "Loaded", ChangedSheets = "ChangedSheets", } /* Engine delegate is the bridge between JS and the rust engine. Primary reason for this class instead of shoving functionality into the engine itself is for performance & reducing amount of data being passed between Rust <-> JS */ export class EngineDelegate { private _listeners: EngineDelegateEventListener[] = []; private _rendered: Record = {}; private _documents: Record = {}; private _graph: Record = {}; constructor( private _native: any, private _io: EngineIO, private _onCrash: (err: any) => void = noop ) { // only one native listener to for buffer performance this._native.add_listener(this._dispatch); this.onEvent(this._onEngineEvent); return this; } resolveFile(fromPath: string, toPath: string) { return this._io.resolveFile(fromPath, toPath); } onEvent(listener: EngineDelegateEventListener) { if (listener == null) { throw new Error(`listener cannot be undefined`); } this._listeners.push(listener); return () => { const i = this._listeners.indexOf(listener); if (i !== -1) { this._listeners.splice(i, 1); } }; } private _onEngineEvent = (event: EngineDelegateEvent) => { if (!this._documents[event.uri]) { this._documents[event.uri] = this._io.readFile(event.uri); } if (event.kind === EngineDelegateEventKind.Deleted) { delete this._rendered[event.uri]; } else if (event.kind === EngineDelegateEventKind.Evaluated) { this._rendered = updateAllLoadedData(this._rendered, event); this._dispatch({ kind: EngineDelegateEventKind.Loaded, uri: event.uri, data: this._rendered[event.uri], }); } else if (event.kind === EngineDelegateEventKind.Diffed) { const existingData = this._rendered[event.uri]; this._rendered = updateAllLoadedData(this._rendered, event); const newData = this._rendered[event.uri]; if ( existingData.kind === EvaluatedDataKind.PC && newData.kind === EvaluatedDataKind.PC ) { this._handlePCDiff(event, existingData, newData); } } }; private _handlePCDiff = ( event: DiffedEvent, existingData: LoadedPCData, newData: LoadedPCData ) => { const removedSheetUris: string[] = []; const diffData = event.data as DiffedPCData; for (const { uri } of existingData.importedSheets) { if (!newData.allImportedSheetUris.includes(uri)) { removedSheetUris.push(uri); } } const addedSheets: SheetInfo[] = []; for ( let i = 0, { length } = diffData.allImportedSheetUris; i < length; i++ ) { const depUri = diffData.allImportedSheetUris[i]; // Note that we only do this if the sheet is already rendered -- engine // doesn't fire an event in that scenario. So we need to notify any listener that a sheet // has been added, including the actual sheet object. if ( !existingData.allImportedSheetUris.includes(depUri) && this._rendered[depUri] ) { addedSheets.push({ uri: depUri, index: i, sheet: this._rendered[depUri].sheet, }); } } if (addedSheets.length || removedSheetUris.length) { this._dispatch({ uri: event.uri, kind: EngineDelegateEventKind.ChangedSheets, data: { // TODO - don't do this - instead include newSheetUris and // allow renderer to fetch these sheets newSheets: addedSheets, removedSheetUris: removedSheetUris, allImportedSheetUris: diffData.allImportedSheetUris, }, }); } }; parseFile(uri: string): Module | ErrorResult { return mapResult(this._native.parse_file(uri)); } lint(uri: string): Diagnostic[] { return this._native.lint_file(uri); } getVirtualNodeSourceInfo( nodePath: number[], uri: string ): VirtualNodeSourceInfo { return this._native.get_virtual_node_source_info(nodePath, uri); } generateCoverageReport(): CoverageReport { return this._tryCatch(() => { return mapResult(this._native.generate_coverage_report()); }); } getLoadedAst(uri: string): DependencyContent { return this._tryCatch(() => this._native.get_loaded_ast(uri)); } getLoadedDependency(uri: string) { return ( this._graph[uri] || (this._graph[uri] = this._tryCatch(() => this._native.get_dependency(uri) )) ); } parseContent(content: string, uri: string): Module | ErrorResult { return this._tryCatch(() => mapResult(this._native.parse_content(content, uri)) ); } purgeUnlinkedFiles() { return this._tryCatch(() => { const ret = mapResult(this._native.purge_unlinked_files()); return ret; }); } getVirtualContent(uri: string) { return this._documents[uri]; } updateVirtualFileContent(uri: string, content: string) { this._graph[uri] = undefined; // only define if successfuly loaded this._documents[uri] = content; return this._tryCatch(() => { const ret = mapResult( this._native.update_virtual_file_content(uri, content) ); return ret; }); } public inspectNodeStyles( source: VirtNodeSource, screenWidth: number ): NodeStyleInspection { return this._native.inspect_node_styles( source.path, source.uri, screenWidth ); } public getLoadedData(uri: string): LoadedData | null { return this._rendered[uri]; } public getExpressionById(id: string): [string, RootExpression] { return this._native.get_expression_by_id(id); } public getAllLoadedData(): Record { return this._rendered; } public getLoadedGraph(): Record { const map = {}; for (const uri in this._rendered) { map[uri] = this.getLoadedDependency(uri); } return map; } reset() { this._rendered = {}; this._documents = {}; this._native.reset(); } open(uri: string): LoadedData { this._graph[uri] = undefined; // need to load document so that it's accessible via source writer if (!this._documents[uri]) { this._documents[uri] = this._io.readFile(uri); } const result = this._tryCatch(() => mapResult(this._native.run(uri))); if (result && result.error) { throw result.error; } return this._rendered[uri]; } private _tryCatch = (fn: () => TRet) => { try { return fn(); } catch (e) { this._onCrash(e); return null; } }; private _dispatch = (event: EngineDelegateEvent) => { // try-catch since engine will throw opaque error. for (const listener of this._listeners) { listener(event); } }; } export type LoadedDataDetails = { src?: string; injectStyles?: boolean; } & LoadedData; /** * Kept separate from the engine since this is more of a util function for ID inspection */ export const getEngineImports = ( uri: string, delegate: EngineDelegate ): Record => { const data = delegate.getLoadedData(uri); if (data?.kind === EvaluatedDataKind.PC) { const ast = delegate.getLoadedAst(uri) as DependencyNodeContent; return Object.keys(data.dependencies).reduce((record: any, id: string) => { const depUri = data.dependencies[id]; const imp = ast && (getImportById(id, ast) || getImportBySrc(id, ast)); record[id] = { uri: imp && getAttributeStringValue("src", imp), injectStyles: imp && hasAttribute(INJECT_STYLES_TAG_NAME, imp), ...delegate.getLoadedData(depUri)!, }; return record; }, {}); } else { return {}; } }; ================================================ FILE: packages/paperclip-core/src/core/index.ts ================================================ export * from "./infer"; export * from "@paperclip-ui/utils"; export * from "./utils"; export { EngineDelegate, EngineMode, EngineOptions, getEngineImports, isErrorResult, ErrorResult } from "./delegate"; ================================================ FILE: packages/paperclip-core/src/core/infer.ts ================================================ import { Node, NodeKind, Element, AttributeKind, AttributeValueKind, Slot, Attribute, Fragment, ScriptExpression, ScriptExpressionKind, DynamicStringAttributeValuePartKind, ReferencePart } from "@paperclip-ui/utils"; // TODO - this should be built in rust export enum InferenceKind { Shape, Array, Any } /* {#each items as item, i} {#each item.friends as friend, j} {friend.name} {/} {/} scope: { item: ['items'], i: [], friend: ['item', 'scope'], j: [] } */ type BaseInference = { kind: TInferenceKind; }; export type ShapeInference = { fromSpread: boolean; properties: { [identifier: string]: ShapeProperty; }; } & BaseInference; export type ShapeProperty = { optional?: boolean; value: Inference; }; export type ArrayInference = { value: Inference; } & BaseInference; export type AnyInference = BaseInference; export type Inference = ShapeInference | ArrayInference | AnyInference; export type Context = { scope: { [identifier: string]: ReferencePart[]; }; inference: Inference; }; const createShapeInference = ( properties: { [identifier: string]: ShapeProperty; } = {}, fromSpread = false ): ShapeInference => ({ kind: InferenceKind.Shape, fromSpread, properties }); const createAnyInference = (): AnyInference => ({ kind: InferenceKind.Any }); const ANY_INFERENCE = createAnyInference(); const SPREADED_SHAPE_INFERENCE = createShapeInference({}, true); const addShapeInferenceProperty = ( part: ReferencePart, value: Inference, shape: ShapeInference ): ShapeInference => ({ ...shape, properties: { ...shape.properties, [part.name]: { value, optional: shape.properties[part.name]?.optional === false ? shape.properties[part.name]?.optional : part.optional } } }); const mergeShapeInference = (existing: ShapeInference, extended: Inference) => { if (extended.kind === InferenceKind.Any) { return existing; } if (extended.kind === InferenceKind.Array) { console.error(`Conflict: can't access properties of arra`); // ERRROR! return existing; } return { ...existing, properties: { ...existing.properties, ...extended.properties } }; }; const addInferenceProperty = ( path: ReferencePart[], value: Inference, owner: Inference, _index = 0 ): Inference => { if (path.length === 0) { return owner; } if (owner.kind === InferenceKind.Any) { owner = createShapeInference(); } const part = path[_index]; if (owner.kind === InferenceKind.Shape) { if (_index < path.length - 1) { let childValue = owner.properties[part.name]?.value || createShapeInference(); childValue = addInferenceProperty(path, value, childValue, _index + 1); owner = addShapeInferenceProperty(part, childValue, owner); } else { const existingInference = owner.properties[part.name]?.value || ANY_INFERENCE; if (existingInference.kind === InferenceKind.Shape) { value = mergeShapeInference(existingInference, value); } owner = addShapeInferenceProperty(part, value, owner); } } if (owner.kind === InferenceKind.Array) { owner = { ...owner, value: addInferenceProperty(path, value, owner.value, _index) }; } return owner; }; const unfurlScopePath = ( path: ReferencePart[], context: Context ): ReferencePart[] => { let cpath = path; if (!context.scope[path[0].name]) { return path; } let entirePath = path; while (true) { const property = cpath[0].name; const newCPath = context.scope[property]; // if exists, but empty, then the scope is created within the template if (newCPath) { if (newCPath.length === 0) { return []; } } else { break; } entirePath = [...newCPath, ...entirePath.slice(1)]; cpath = newCPath; } return entirePath; }; const addContextInferenceProperty = ( path: ReferencePart[], value: Inference, context: Context ) => ({ ...context, inference: addInferenceProperty( unfurlScopePath(path, context), value, context.inference ) }); export const infer = (ast: Node): ShapeInference => { return inferNode(ast, true, { scope: {}, inference: createShapeInference() }).inference as ShapeInference; }; const inferNode = (ast: Node, isRoot: boolean, context: Context): Context => { switch (ast.nodeKind) { case NodeKind.Element: return inferElement(ast, isRoot, context); case NodeKind.Slot: return inferSlot(ast, context); case NodeKind.Fragment: return inferFragment(ast, context); } return context; }; const inferElement = (element: Element, isRoot: boolean, context: Context) => { for (const atttribute of element.attributes) { context = inferAttribute(atttribute, context); } context = inferChildren(element.children, context); return context; }; const inferAttribute = (attribute: Attribute, context: Context) => { switch (attribute.attrKind) { case AttributeKind.KeyValueAttribute: { if ( attribute.value && attribute.value.attrValueKind === AttributeValueKind.Slot ) { context = inferScriptExpression(attribute.value.script, context); } if ( attribute.value && attribute.value.attrValueKind === AttributeValueKind.DyanmicString ) { for (const part of attribute.value.values) { if (part.partKind === DynamicStringAttributeValuePartKind.Slot) { context = inferScriptExpression(part, context); } } } break; } case AttributeKind.PropertyBoundAttribute: { context = addContextInferenceProperty( [{ name: attribute.bindingName, optional: true }], ANY_INFERENCE, context ); break; } case AttributeKind.ShorthandAttribute: { context = inferScriptExpression(attribute.reference, context); break; } case AttributeKind.SpreadAttribute: { context = inferScriptExpression( attribute.script, context, SPREADED_SHAPE_INFERENCE ); break; } } return context; }; const inferSlot = (slot: Slot, context: Context) => { return inferScriptExpression(slot.script, context); }; const inferFragment = (fragment: Fragment, context: Context) => { return inferChildren(fragment.children, context); }; const inferChildren = (children: Node[], context: Context) => children.reduce( (context, child) => inferNode(child, false, context), context ); const inferScriptExpression = ( expression: ScriptExpression, context: Context, defaultInference: Inference = ANY_INFERENCE ) => { switch (expression.scriptKind) { case ScriptExpressionKind.Reference: { context = addContextInferenceProperty( expression.path, defaultInference, context ); break; } case ScriptExpressionKind.Node: { context = inferNode(expression, false, context); break; } case ScriptExpressionKind.Object: { for (const property of expression.properties) { context = inferScriptExpression( property.value, context, defaultInference ); } break; } case ScriptExpressionKind.Array: { for (const value of expression.values) { context = inferScriptExpression(value, context, defaultInference); } break; } case ScriptExpressionKind.Conjunction: { context = inferScriptExpression( expression.left, context, defaultInference ); context = inferScriptExpression( expression.right, context, defaultInference ); break; } case ScriptExpressionKind.Not: { context = inferScriptExpression( expression.expression, context, defaultInference ); break; } } return context; }; ================================================ FILE: packages/paperclip-core/src/core/utils.ts ================================================ import * as path from "path"; // import _FileSystem from "fs"; import { findPCConfigUrl, PaperclipConfig, PC_CONFIG_FILE_NAME, } from "@paperclip-ui/utils"; import { fileURLToPath, pathToFileURL } from "@paperclip-ui/utils/lib/core/url"; export type FileSystem = { realpathSync(uri: string | URL): string; existsSync(uri: string | URL): boolean; readFileSync(uri: string | URL, encoding: string): string; lstatSync(uri: string | URL): any; readdirSync(uri: string | URL): string[]; }; // TODO - move to @paperclip-ui/utils as soon as we have a glob library that can handle virtual file systems const findResourcesFromConfig = ( getResources: ( fs: FileSystem ) => (config: PaperclipConfig, options: any) => string[] ) => (fs: FileSystem) => (fromUri: string, relative?: boolean) => { // symlinks may fudge module resolution, so we need to find the real path const fromPath = fs.realpathSync(new URL(fromUri)); const fromPathDirname = path.dirname(fromPath); const configUrl = findPCConfigUrl(fs)(fromUri); // If there's no config, then don't bother looking for PC files. Otherwise we're likely // to need esoteric logic for resolving PC that I don't think should be supported -- there should // just be aproach. if (!configUrl) { return []; } const configUri = new URL(configUrl); const config: PaperclipConfig = JSON.parse( fs.readFileSync(configUri, "utf8") ); return getResources(fs)(config, path.dirname(fileURLToPath(configUri))) .filter((pathname) => pathname !== fromPath) .map((pathname) => { if (relative) { const modulePath = getModulePath( configUrl, config, pathname, fromPathDirname ); if (!path.isAbsolute(modulePath)) { return modulePath; } let relativePath = path.relative(fromPathDirname, modulePath); if (relativePath.charAt(0) !== ".") { relativePath = "./" + relativePath; } return relativePath; } return pathToFileURL(pathname).href; }) .map((filePath) => { return filePath.replace(/\\/g, "/"); }); }; const resolveModuleRoots = (fs) => (fromDir: string, roots: string[] = []) => { const stat = fs.lstatSync(fromDir); const realpath = stat.isSymbolicLink() ? fs.realpathSync(fromDir) : fromDir; const newStat = realpath === fromDir ? stat : fs.lstatSync(realpath); if (!newStat.isDirectory()) { return roots; } if (fs.existsSync(path.join(fromDir, "package.json"))) { roots.push(fromDir); } else { for (const dirname of fs.readdirSync(realpath)) { resolveModuleRoots(fs)(path.join(fromDir, dirname), roots); } } return roots; }; const filterAllFiles = (fs: FileSystem) => (filter: (filePath: string) => boolean) => { const scan = (currentPath: string, results: string[] = []) => { if (!fs.existsSync(currentPath)) { return; } const stat = fs.lstatSync(currentPath); const realpath = stat.isSymbolicLink() ? fs.realpathSync(currentPath) : currentPath; const newStat = realpath === currentPath ? stat : fs.lstatSync(realpath); if (newStat.isDirectory()) { for (const dirname of fs.readdirSync(realpath)) { const dirpath = path.join(currentPath, dirname); scan(dirpath, results); } } else { if (filter(currentPath)) { results.push(currentPath); } } return results; }; return scan; }; const resolveResources = (fs: FileSystem) => ( config: PaperclipConfig, cwd: string, filterFiles: (dir: string) => string[] ) => { const sourceDir = config.srcDir === "." ? cwd : path.join(cwd, config.srcDir); const filePaths = filterFiles(sourceDir) || []; if (config.moduleDirs) { for (const modulesDirname of config.moduleDirs) { const moduleDirPath = path.join(cwd, modulesDirname); const moduleRoots = resolveModuleRoots(fs)(moduleDirPath); for (const moduleDir of moduleRoots) { // need to scan until there's a package. This covers @organization namespaces. if (!moduleDir) { continue; } const pcConfigPath = path.join(moduleDir, PC_CONFIG_FILE_NAME); if (!fs.existsSync(pcConfigPath)) { continue; } const moduleConfig: PaperclipConfig = JSON.parse( fs.readFileSync(pcConfigPath, "utf8") ); const moduleSources = filterFiles( path.join(moduleDir, moduleConfig.srcDir) ); filePaths.push(...moduleSources); } } } return filePaths; }; export const resolveAllPaperclipFiles = findResourcesFromConfig( (fs: FileSystem) => (config, cwd) => { return resolveResources(fs)( config, cwd, filterAllFiles(fs)( (filePath) => path.extname(filePath) === ".pc" || path.extname(filePath) === ".css" ) ); } ); export const resolveAllAssetFiles = findResourcesFromConfig( (fs: FileSystem) => (config, cwd) => { // const ext = `+(jpg|jpeg|png|gif|svg)`; const exts = [".jpg", ".jpeg", ".png", ".gif", ".svg", ".ttf"]; // const sourceDir = config.srcDir; return resolveResources(fs)( config, cwd, filterAllFiles(fs)((filePath) => exts.includes(path.extname(filePath))) ); // if (sourceDir === ".") { // return filterAllFiles(filePath => exts.includes(path.extname(filePath)))(cwd); // // return glob.sync(`**/*.${ext}`, { cwd, realpath: true }); // } // // return glob.sync(`${sourceDir}/**/*.${ext}`, { cwd, realpath: true }); // return filterAllFiles(filePath => exts.includes(path.extname(filePath)))(path.join(cwd, sourceDir)); } ); const getModulePath = ( configUri: string, config: PaperclipConfig, fullPath: string, fromDir?: string ) => { const configDir = path.dirname(fileURLToPath(configUri)); const moduleDirectory = path.join(configDir, config.srcDir) + "/"; if (fullPath.indexOf(moduleDirectory) === 0) { const modulePath = fullPath.replace(moduleDirectory, ""); const relativePath = fromDir && path.relative(fromDir, fullPath); if (relativePath?.match(/\.\.\//g)?.length || 0 > 0) { return modulePath; } } if (config.moduleDirs) { for (const moduleDirectory of config.moduleDirs) { const fullModulePath = path.join(configDir, moduleDirectory); if (fullPath.indexOf(fullModulePath) === 0) { return fullPath.replace(fullModulePath, "").substr(1); } } } return fullPath; }; // eslint-disable-next-line export const noop = () => {}; ================================================ FILE: packages/paperclip-core/src/node/async-delegate.ts ================================================ export class AsyncEngineDelegate { private _worker: Worker; constructor() { const worker = (this._worker = new Worker( __dirname + "/delegate-worker.js" )); worker.onmessage = this._onMessage; } // eslint-disable-next-line private _onMessage(message: any) {} } ================================================ FILE: packages/paperclip-core/src/node/delegate-worker.ts ================================================ ================================================ FILE: packages/paperclip-core/src/node/index.ts ================================================ import { PaperclipResourceWatcher, ChangeKind } from "@paperclip-ui/utils/lib/node/source-watcher"; export * from "./sync-delegate"; export * from "./async-delegate"; export { PaperclipResourceWatcher, ChangeKind }; ================================================ FILE: packages/paperclip-core/src/node/sync-delegate.ts ================================================ const { NativeEngine } = require("../../native/node/paperclip"); import * as fs from "fs"; import { URL, fileURLToPath } from "url"; import * as path from "path"; import { EngineDelegate, EngineMode } from "../core"; import { resolveImportUri, resolvePCConfig } from "@paperclip-ui/utils"; const existsSyncCaseSensitive = (uri) => { const pathname = fileURLToPath(String(uri)); const dir = path.dirname(pathname); const basename = path.basename(pathname); if (!fs.existsSync(dir)) { return false; } return fs.readdirSync(dir).includes(basename); }; const getIOOptions = (options) => { const resolveFile = resolveImportUri(fs); return Object.assign( { readFile: (uri) => { // eslint-disable-next-line // ts-ignore return fs.readFileSync(new URL(uri), "utf8"); }, fileExists: (uri) => { try { // eslint-disable-next-line const url = new URL(uri); // need to make sure that case matches _exactly_ since some // systems are sensitive to that. return existsSyncCaseSensitive(url) && fs.lstatSync(url).isFile(); } catch (e) { // eslint-disable-next-line console.error(e); return false; } }, resolveFile: (from, to) => { // TRUE boolean flag necessary here to resolve symlinks. return resolveFile(from, to); }, getLintConfig: (uri) => { const info = resolvePCConfig(fs)(uri); if (!info) { return null; } return info[0].lintOptions; }, mode: EngineMode.SingleFrame, }, options.io, { mode: options.mode, includedUsedExpressions: options.includeUsedExpressions, } ); }; // eslint-disable-next-line export const createEngineDelegate = (options = {}, onCrash: any = () => {}) => { const io = getIOOptions(options || {}); const { readFile, fileExists, resolveFile, getLintConfig, mode = EngineMode.SingleFrame, } = io; return new EngineDelegate( NativeEngine.new(readFile, fileExists, resolveFile, getLintConfig, mode), io, onCrash || function (e) { console.error(e); } ); }; export const loadEngineDelegate = createEngineDelegate; ================================================ FILE: packages/paperclip-core/src/test/file-system/pc-test.ts ================================================ import * as path from "path"; import * as url from "url"; import { expect } from "chai"; import { createEngineDelegate } from "../../node"; import { stringifyLoadResult, TEST_FIXTURE_SRC_DIRECTORY } from "../utils"; describe(__filename + "#", () => { it("Can load an entry that has an import", async () => { const e = createEngineDelegate(); const result = stringifyLoadResult( await e.open( url .pathToFileURL( path.join(TEST_FIXTURE_SRC_DIRECTORY, "good-import.pc") ) .toString() ) ); expect(result.replace(/ class=".*?"/g, "")).to.eql( `
    I'm a secret!
    ` ); }); it("Won't load module src where the casing is incorrect", async () => { const e = createEngineDelegate(); let err; try { e.open( url .pathToFileURL(path.join(TEST_FIXTURE_SRC_DIRECTORY, "bad-import.pc")) .toString() ); } catch (e) { err = e; } expect(err.errorKind).to.eql("Graph"); expect(err.info.message).to.eql("import not found"); }); it("Displays an error for 404 CSS url", async () => { const e = createEngineDelegate(); let err; try { await e.open( url .pathToFileURL( path.join(TEST_FIXTURE_SRC_DIRECTORY, "bad-css-url.pc") ) .toString() ); } catch (e) { err = e; } expect(err).not.to.eq(null); expect(err.errorKind).to.eql("Runtime"); expect(err.message).to.contain("Unable to resolve file: /not/found.png"); }); it("can resolve a pc file from a a module", async () => { const e = createEngineDelegate(); const result = await e.open( url .pathToFileURL(path.join(TEST_FIXTURE_SRC_DIRECTORY, "mod-a-import.pc")) .toString() ); expect(stringifyLoadResult(result).replace(/ class=".*?"/g, "")).to.eql( `
    Some Module
    from test.pc
    ` ); }); it("can resolve a pc file from a nested module", async () => { const e = createEngineDelegate(); const result = await e.open( url .pathToFileURL( path.join(TEST_FIXTURE_SRC_DIRECTORY, "nested-mod-import.pc") ) .toString() ); expect(stringifyLoadResult(result).replace(/ class=".*?"/g, "")).to.eql( `
    Some Module
    from test.pc
    ` ); }); }); ================================================ FILE: packages/paperclip-core/src/test/file-system/resolve-test.ts ================================================ import * as fs from "fs"; import * as path from "path"; import * as url from "url"; import { expect } from "chai"; import { TEST_FIXTURE_SRC_DIRECTORY } from "../utils"; import { resolveAllPaperclipFiles } from "../../core/utils"; import { resolveImportUri } from "@paperclip-ui/utils"; describe(__filename + "#", () => { it("returns nested module when resolving", async () => { const results = resolveAllPaperclipFiles(fs as any)( url.pathToFileURL( path.join(TEST_FIXTURE_SRC_DIRECTORY, "nested-mod-import.pc") ).href, true ); expect( results.map((path) => { return path.replace(/^\.\//, ""); }) ).to.eql([ "bad-css-url.pc", "bad-import.pc", "good-import.pc", "hello-world.pc", "mod-a-import.pc", "mod-import.pc", "module.pc", "@nested/in/a/folder/src/component.pc", "@nested/in/a/folder/src/imp-mod-a.pc", "@nested/in/a/folder/src/module.pc", "@nested/in/a/folder/src/test.pc", "module-a/src/component.pc", "module-a/src/module.pc", "module-a/src/test.pc", ]); }); it("traverses parent to resolve module", async () => { const outputPath = resolveImportUri(fs)( path.join( TEST_FIXTURE_SRC_DIRECTORY, "../modules/@nested/in/a/folder/src/imp-mod-a.pc" ), "module-a/src/module.pc" ); expect(outputPath).to.contain("modules/module-a/src/module.pc"); }); }); ================================================ FILE: packages/paperclip-core/src/test/utils.ts ================================================ import * as path from "path"; import { createEngineDelegate } from "../node"; import { EngineDelegate, EngineMode } from "../core"; import { EngineErrorEvent, EngineDelegateEventKind, stringifyVirtualNode, stringifyCSSSheet, LoadedEvent, LoadedData, EvaluatedDataKind } from "@paperclip-ui/utils"; export type Graph = { [identifier: string]: string; }; export const TEST_FIXTURE_SRC_DIRECTORY = path.join( __dirname, "../../test-fixtures/src" ); export const createMockEngine = ( graph: Graph, onErr = e => console.error(e), io: Partial = {}, mode = EngineMode.SingleFrame ) => createEngineDelegate( { io: { readFile: uri => { return graph[uri]; }, fileExists: uri => { return graph[uri] != null; }, resolveFile: (from, to) => { if (to.charAt(0) === "/") { return to; } return path.join(path.dirname(from), to).replace(/\\/g, "/"); }, getLintConfig: () => { return { noUnusedStyles: true, enforceVars: ["font-family", "padding", "color"] }; }, ...io }, mode }, onErr ); export const waitForError = ( engine: EngineDelegate, test: (event: EngineErrorEvent) => boolean = () => true ) => { return new Promise(resolve => { engine.onEvent(event => { if (event.kind === EngineDelegateEventKind.Error && test(event)) { resolve(event); } }); }); }; export const waitForRender = ( engine: EngineDelegate, test: (event: LoadedEvent) => boolean = () => true ) => { return new Promise(resolve => { engine.onEvent(event => { if (event.kind === EngineDelegateEventKind.Loaded && test(event)) { resolve(event); } }); }); }; export const stringifyLoadResult = ( data: LoadedData, shouldCleanHTML = true ) => { if (data.kind === EvaluatedDataKind.PC) { const { sheet, preview, importedSheets: sheets } = data; const sheetText = [...sheets.map(({ sheet }) => sheet), sheet] .map(sheet => { return stringifyCSSSheet(sheet, { resolveUrl: url => url.replace("file://", "") }); }) .join("\n") .trim(); const buffer = `${stringifyVirtualNode( preview )}`; return shouldCleanHTML ? cleanHTML(buffer) : buffer; } else { return ""; } }; export const cleanHTML = (value: string) => { return value.replace(/[\r\n\t\s]+/g, " ").trim(); }; // eslint-disable-next-line export const noop = () => {}; ================================================ FILE: packages/paperclip-core/src/test/virtual/annotate-test.ts ================================================ import { createMockEngine } from "../utils"; import { expect } from "chai"; import { Comment, computeVirtScriptObject, Fragment } from "@paperclip-ui/utils"; import { EngineMode } from "../../core"; describe(__filename + "#", () => { it("can parse a @desc", async () => { const graph = { "/entry.pc": `
    something ` }; const engine = await createMockEngine(graph); const result = engine.open("/entry.pc"); expect( (result as any).preview.children[0].annotations.values.desc.value ).to.eql("some description"); }); it(`Can add metadata to an instance`, async () => { const graph = { "/entry.pc": ` Hello ` }; const engine = await createMockEngine(graph); const result = engine.open("/entry.pc"); expect( (result as any).preview.children[0].annotations.values.desc.value ).to.eql("abc"); }); it(`Can add metadata to an imported instance`, async () => { const graph = { "/entry.pc": ` `, "/module.pc": ` Hello ` }; const engine = await createMockEngine(graph); const result = engine.open("/entry.pc"); expect( (result as any).preview.children[0].annotations.values.desc.value ).to.eql("abc"); }); it(`properly updates annotations`, async () => { const graph = { "/entry.pc": ` hello ` }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect( (result as any).preview.children[0].annotations.values.frame.value ).to.eql("a"); engine.updateVirtualFileContent( "/entry.pc", ` hello ` ); const result2 = await engine.open("/entry.pc"); expect( (result2 as any).preview.children[0].annotations.values.frame.value ).to.eql("b"); }); it("Can have multiple annotations", async () => { const graph = { "/entry.pc": ` Hello ` }; const engine = await createMockEngine(graph); const result = engine.open("/entry.pc"); const metadata = computeVirtScriptObject( (result as any).preview.children[0].annotations ); expect(metadata).to.eql({ tags: { a: "b" }, frame: { x: -1145, y: -28, title: "Preview", width: 1024, height: 768 } }); }); it("Does not render invisible component frames", async () => { const graph = { "/entry.pc": ` Hello ` }; const engine = await createMockEngine( graph, null, {}, EngineMode.MultiFrame ); const result = engine.open("/entry.pc"); expect((result as any).preview.children.length).to.eql(1); }); it("Does not render invisible element frames", async () => { const graph = { "/entry.pc": ` Hello
    ` }; const engine = await createMockEngine( graph, null, {}, EngineMode.MultiFrame ); const result = engine.open("/entry.pc"); expect((result as any).preview.children.length).to.eql(1); }); it("Does not render invisible text elements", async () => { const graph = { "/entry.pc": ` Hello
    ` }; const engine = await createMockEngine( graph, null, {}, EngineMode.MultiFrame ); const result = engine.open("/entry.pc"); expect((result as any).preview.children.length).to.eql(1); }); it("Can escape @ signs", async () => { const graph = { "/entry.pc": ` Hello ` }; const engine = await createMockEngine( graph, null, {}, EngineMode.MultiFrame ); engine.open("/entry.pc"); const ast = engine.getLoadedAst("/entry.pc") as Fragment; expect((ast.children[0] as Comment).annotation.properties).to.eql([ { kind: "Text", raws: { before: "", after: "" }, value: "\\@frame", range: { start: { pos: 14, line: 3, column: 13 }, end: { pos: 21, line: 3, column: 20 } } } ]); }); it("Can comment out HTML comments", async () => { const graph = { "/entry.pc": ` Hello ` }; const engine = await createMockEngine( graph, null, {}, EngineMode.MultiFrame ); engine.open("/entry.pc"); const ast = engine.getLoadedAst("/entry.pc") as Fragment; expect((ast.children[0] as Comment).annotation.properties).to.eql([ { kind: "Text", raws: { before: "", after: "" }, value: 'span component as="Test">\n Hello\n { [ // various [`.a { color: red; }`, `[class]._80f4925f_a { color:red; }`, false], [ `.a, .b { color: red; }`, `[class]._80f4925f_a { color:red; } [class]._80f4925f_b { color:red; }`, false ], [ `.a.b { color: red; }`, `._80f4925f_a._80f4925f_b._80f4925f { color:red; }`, false ], [ `.a .b { color: red; }`, `[class]._80f4925f_a [class]._80f4925f_b { color:red; }`, false ], [`:global(.a) { color: red; }`, `.a { color:red; }`, false], [ `:global(.a, .b) .c { color: red; }`, `.a [class]._80f4925f_c { color:red; } .b [class]._80f4925f_c { color:red; }`, false ], [ `.c :global(.a, .b) { color: red; }`, `[class]._80f4925f_c .a { color:red; } [class]._80f4925f_c .b { color:red; }`, false ], [ `a { b, c { d { color: red; }} }`, `a._80f4925f b._80f4925f d._80f4925f { color:red; } a._80f4925f c._80f4925f d._80f4925f { color:red; }`, false ], [ `.c :global(.a, :global(.a1, .a2), .b) { color: red; }`, `[class]._80f4925f_c .a { color:red; } [class]._80f4925f_c .a1 { color:red; } [class]._80f4925f_c .a2 { color:red; } [class]._80f4925f_c .b { color:red; }`, false ], [ `.c:within(.b) { color: red; }`, `[class]._80f4925f_b ._80f4925f_c._80f4925f { color:red; }`, false ], [ `.c:within(.b, .d) { color: red; }`, `[class]._80f4925f_b ._80f4925f_c._80f4925f { color:red; } [class]._80f4925f_d ._80f4925f_c._80f4925f { color:red; }`, false ], [ `.c:within(.b, .d) { color: red; }`, `[class]._80f4925f_b ._406d2856 ._80f4925f_c._80f4925f { color:red; } [class]._80f4925f_d ._406d2856 ._80f4925f_c._80f4925f { color:red; }`, true ], [ `.c { color: red; }`, `._406d2856 [class]._80f4925f_c { color:red; }`, true ], [ `&:within(.a) { color: red}`, `[class]._80f4925f_a ._406d2856._406d2856 { color:red; }`, true ], [ `:self:within(.a) { color: red}`, `[class]._80f4925f_a ._406d2856._406d2856._80f4925f { color:red; }`, true ], // [ // `:within(.a):self { color: red}`, // `[class]._80f4925f_a ._406d2856._406d2856._80f4925f { color:red; }`, // true // ], // [ // `:within(:global(.a, .b)):self { color: red}`, // `[class].a ._406d2856._406d2856._80f4925f { color:red; } [class].b ._406d2856._406d2856._80f4925f { color:red; }`, // true // ], [ `.a, &:within(.b) { color: red}`, `._406d2856 [class]._80f4925f_a { color:red; } [class]._80f4925f_b ._406d2856._406d2856 { color:red; }`, true ], [ `.a { .b { color: blue; }}`, `[class]._80f4925f_a [class]._80f4925f_b { color:blue; }`, false ], [ `.a { &--b { color: blue; }}`, `[class]._80f4925f_a--b { color:blue; }`, false ], [ `.a { &.b { color: blue; }}`, `[class]._80f4925f_a._80f4925f_b { color:blue; }`, false ], [ `.a { && { &&& { color: blue; } }}`, `[class]._80f4925f_a[class]._80f4925f_a[class]._80f4925f_a[class]._80f4925f_a[class]._80f4925f_a[class]._80f4925f_a { color:blue; }`, false ], [ `.a { & & { &.test { color: blue; } }}`, `[class]._80f4925f_a [class]._80f4925f_a._80f4925f_test { color:blue; }`, false ], [ `&:within(.a) { && { color: blue; }}`, `[class]._80f4925f_a ._406d2856._406d2856._406d2856._406d2856 { color:blue; }`, true ], [ `element.a { color: red; }`, `element._80f4925f_a._80f4925f { color:red; }`, false ], [`:global(element.b) { color: red; }`, `element.b { color:red; }`, false], [ `a ~ b { color: red; }`, `a._80f4925f ~ b._80f4925f { color:red; }`, false ], [ `a > b { color: red; }`, `a._80f4925f > b._80f4925f { color:red; }`, false ], [`* { color: red; }`, `._80f4925f { color:red; }`, false], [`:global(*) { color: red; }`, `* { color:red; }`, false], [`:not(a) { color: red; }`, `._80f4925f:not(a) { color:red; }`, false], [`a:not(a) { color: red; }`, `a:not(a)._80f4925f { color:red; }`, false], [ `:not(:within(.a)) { color: red; }`, `:not([class]._80f4925f_a) ._80f4925f { color:red; }`, false ], [ `:not(:within(.a, .b)) { color: red; }`, `:not([class]._80f4925f_a) ._80f4925f { color:red; } :not([class]._80f4925f_b) ._80f4925f { color:red; }`, false ], [ `:not(:within(:global(.a, .b))) { color: red; }`, `:not(.a) ._80f4925f { color:red; } :not(.b) ._80f4925f { color:red; }`, false ], [ `:within(:not(.a)) { color: red; }`, `._80f4925f:not(._80f4925f_a) ._80f4925f { color:red; }`, false ], [ `[data-test] { color: red; }`, `[data-test]._80f4925f { color:red; }`, false ], [ `[data-test=b] { color: red; }`, `[data-test=b]._80f4925f { color:red; }`, false ], [ `[data-test^=b] { color: red; }`, `[data-test^=b]._80f4925f { color:red; }`, false ], [ `[data-test~=b] { color: red; }`, `[data-test~=b]._80f4925f { color:red; }`, false ], [ `a:nth-child(1) { color: red; }`, `a._80f4925f:nth-child(1) { color:red; }`, false ], [ `a:nth-child(1):hover { color: red; }`, `a._80f4925f:nth-child(1):hover { color:red; }`, false ], [`#id { color: red; }`, `#id._80f4925f { color:red; }`, false], [`:global(#id) { color: red; }`, `#id { color:red; }`, false], // needs to emit 4 selectors [ `&:within(a, b):within(c, d) { color: orange; }`, `a._80f4925f ._406d2856._406d2856 { color:orange; } b._80f4925f ._406d2856._406d2856 { color:orange; } c._80f4925f ._406d2856._406d2856 { color:orange; } d._80f4925f ._406d2856._406d2856 { color:orange; }`, true ], [ `a.b.c:within(:global(d, e)) { color: orange; }`, `d ._406d2856 a._80f4925f_b._80f4925f_c._80f4925f { color:orange; } e ._406d2856 a._80f4925f_b._80f4925f_c._80f4925f { color:orange; }`, true ], [ `&.a { color: red}`, `._406d2856._406d2856._80f4925f_a { color:red; }`, true ], [ `:global(a, &:within(b, c, d)) { color: orange; }`, `._406d2856 a { color:orange; } b._80f4925f ._406d2856._406d2856 { color:orange; } c._80f4925f ._406d2856._406d2856 { color:orange; } d._80f4925f ._406d2856._406d2856 { color:orange; }`, true ], // https://github.com/paperclipui/paperclip/issues/547 [ `:self(.a) { &&& { color: red; &:checked { color: blue; }}}`, `._406d2856._406d2856._80f4925f_a._406d2856._406d2856._80f4925f_a._406d2856._406d2856._80f4925f_a { color:red; } ._406d2856._406d2856._80f4925f_a._406d2856._406d2856._80f4925f_a._406d2856._406d2856._80f4925f_a:checked { color:blue; }`, true ], // https://github.com/paperclipui/paperclip/issues/549 [ `:self:empty { color: red; }`, `._406d2856._406d2856._80f4925f:empty { color:red; }`, true ], // https://github.com/paperclipui/paperclip/issues/548 [ `:self > * { color: red; }`, `._406d2856._406d2856 > ._80f4925f { color:red; }`, true ], [ `:root { color: red; }`, `._80f4925f:not(._80f4925f ._80f4925f) { color:red; }`, false ], [`:global(:root) { color: red; }`, `:root { color:red; }`, false], // https://github.com/paperclipui/paperclip/issues/573 [ `:self(.variant) { @media screen and (max-width: 400px) { color: red; }}`, `@media screen and (max-width: 400px) { ._406d2856._406d2856._80f4925f_variant { color:red; } }`, true ], // https://github.com/paperclipui/paperclip/issues/607#issuecomment-758309798 [ `div { :within(.a) { color: red }}`, `[class]._80f4925f_a div._80f4925f ._80f4925f { color:red ; }`, false ], // https://github.com/paperclipui/paperclip/issues/607#issuecomment-758309798 [ `:within(.a) { color: red }`, `[class]._80f4925f_a ._406d2856 ._80f4925f { color:red ; }`, true ], // https://github.com/paperclipui/paperclip/issues/721 [ `&:within(.variant) { transform: translateX(100%); &:within(.visible) { transform: translateX(0%); } }`, `[class]._80f4925f_variant ._406d2856._406d2856 { transform:translateX(100%); } [class]._80f4925f_variant[class]._80f4925f_visible ._406d2856._406d2856 { transform:translateX(0%); }`, true ], // .variant .visible [ `&:within(.variant) { transform: translateX(100%); :within(.visible) { transform: translateX(0%); } }`, `[class]._80f4925f_variant ._406d2856._406d2856 { transform:translateX(100%); } [class]._80f4925f_variant [class]._80f4925f_visible ._406d2856._406d2856 ._80f4925f { transform:translateX(0%); }`, true ], [ `&:within(.a) { transform: translateX(100%); &:within(.b) { :within(.c, .d) { color: red; }} }`, `[class]._80f4925f_a ._406d2856._406d2856 { transform:translateX(100%); } [class]._80f4925f_a[class]._80f4925f_b [class]._80f4925f_c ._406d2856._406d2856 ._80f4925f { color:red; } [class]._80f4925f_a[class]._80f4925f_b [class]._80f4925f_d ._406d2856._406d2856 ._80f4925f { color:red; }`, true ], [ `:within(.a) { transform: translateX(100%); &:within(.b) { color: red; } }`, `[class]._80f4925f_a ._406d2856 ._80f4925f { transform:translateX(100%); } [class]._80f4925f_a[class]._80f4925f_b ._406d2856 ._80f4925f { color:red; }`, true ], [ `&:has(.a) { color: red; }`, `._406d2856._406d2856:has([class]._80f4925f_a) { color:red; }`, true ], [ `&:has(:global(.a)) { color: red; }`, `._406d2856._406d2856:has(.a) { color:red; }`, true ], [ `a:before { color: red; }`, `._406d2856 a._80f4925f:before { color:red; }`, true ], [ `@charset "utf-8"; div { color: red; }`, `@charset "utf-8"; div._80f4925f { color:red; }`, false ], [ `@export { :root { color: red; }}`, `._pub-80f4925f:not(._pub-80f4925f ._pub-80f4925f) { color:red; }`, false ], [ `@media screen { @media print { .a { color: red; }}}`, `@media screen { @media print { [class]._80f4925f_a { color:red; } } }`, false ], [`.a\\:b { color: red; }`, `[class]._80f4925f_a\\:b { color:red; }`, false], // fix https://github.com/paperclipui/paperclip/issues/966 [ `html, body { color: red; }`, `._80f4925f:not(._80f4925f ._80f4925f) { color:red; } ._80f4925f:not(._80f4925f ._80f4925f) { color:red; }`, false ] // group, selector ].forEach(([input, output, scoped]) => { it(`compiles ${input} -> ${output}`, async () => { let source = ``; if (scoped) { source = `
    ${source}
    `; } const engine = await createMockEngine({ "/entry.pc": source }); const text = stringifyLoadResult(await engine.open("/entry.pc")).match( /`, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql(""); }); it("displays an error if style url not found", async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph, noop, { resolveFile() { return null; }, }); let err; try { engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 59, line: 4, column: 25 }, end: { pos: 91, line: 5, column: 11 }, }, message: "Unable to resolve file: /not/found.png", }); }); it("can resolve some relative urls", async () => { const graph = { "/entry.pc": ` `, "/path/to/test/test.woff": "", }; const engine = await createMockEngine(graph, noop, { resolveFile() { return "/path/to/test/test.woff"; }, }); const result = engine.open("/entry.pc"); expect(stringifyCSSSheet(result.sheet).replace(/[\n\s]+/g, " ")).to.eql( `[class]._80f4925f_rule { background:url('/path/to/test/test.woff'); src:url("http://google.com"); }` ); }); describe("Mixins", () => { it("can be created & used", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql(""); }); it("Displays an error if a mixin is not found", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 45, line: 3, column: 22 }, end: { pos: 46, line: 3, column: 23 }, }, message: "Reference not found.", }); }); it("can use an imported mixin", async () => { const graph = { "/entry.pc": ``, "/module.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql(""); }); it("Displays an error if an imported mixin is not found", async () => { const graph = { "/entry.pc": ``, "/module.pc": ``, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 87, line: 3, column: 26 }, end: { pos: 88, line: 3, column: 27 }, }, message: "Reference not found.", }); }); it("Displays an error if the import is not found", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 45, line: 3, column: 22 }, end: { pos: 48, line: 3, column: 25 }, }, message: "Reference not found.", }); }); // expectation is still incorrect, just want to make sure that this doesn't break the engine it("Smoke -- can use nested refs", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 45, line: 3, column: 22 }, end: { pos: 46, line: 3, column: 23 }, }, message: "Reference not found.", }); }); it("Displays an error if a mixin is used but not exported", async () => { const graph = { "/entry.pc": ``, "/module.pc": ``, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 87, line: 3, column: 26 }, end: { pos: 92, line: 3, column: 31 }, }, message: "This mixin is private.", }); }); it("Display an error if a mixins is already defined in the upper scope", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 98, line: 6, column: 18 }, end: { pos: 103, line: 6, column: 23 }, }, message: "This mixin is already declared in the upper scope.", }); }); it("properly concats using multiple &", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `` ); }); }); it("can use escape key in class", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `` ); }); it("can use single line comment", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `` ); }); it("errors if comment is unterminated", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Graph", uri: "/entry.pc", info: { kind: "Unterminated", message: "Unterminated element.", range: { start: { pos: 0, line: 1, column: 1 }, end: { pos: 7, line: 1, column: 8 }, }, }, }); }); it("CSS vars are collected in the evaluated output", async () => { const graph = { "/entry.pc": `ab`, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect((result as LoadedPCData).exports.style.variables["--color"]).to.eql({ name: "--color", value: "test", source: { uri: "/entry.pc", range: { start: { pos: 37, line: 3, column: 11 }, end: { pos: 51, line: 3, column: 25 }, }, }, }); }); it("CSS class names are pulled out", async () => { const graph = { "/entry.pc": `ab`, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect((result as LoadedPCData).exports.style.classNames).to.eql({ color: { name: "color", public: false, scopedName: "_80f4925f_color" }, div: { name: "div", public: true, scopedName: "_pub-80f4925f_div" }, child: { name: "child", public: false, scopedName: "_80f4925f_child" }, "element--child": { name: "element--child", public: false, scopedName: "_80f4925f_element--child", }, element: { name: "element", public: false, scopedName: "_80f4925f_element", }, }); }); it("maintains space with & selector", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `` ); }); it("can parse nested tag selectors", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `` ); }); it("can render keyframes with a dash in the name", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `` ); }); it("can evaluated multiple nested selectors without &", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `` ); }); it("AST location is correct with unicode characters", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); await engine.open("/entry.pc"); const ast = (await engine.getLoadedAst("/entry.pc")) as any; expect(ast.children[0].sheet.rules[1].range).to.eql({ start: { pos: 88, line: 7, column: 7 }, end: { pos: 111, line: 9, column: 5 }, }); }); it("includes keyframes in export", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect((result as LoadedPCData).exports.style.keyframes).to.eql({ a: { name: "a", public: false, source: { uri: "/entry.pc", range: { start: { pos: 25, line: 2, column: 18, }, end: { pos: 44, line: 5, column: 7, }, }, }, }, b: { name: "b", public: true, source: { uri: "/entry.pc", range: { start: { pos: 73, line: 6, column: 20, }, end: { pos: 94, line: 9, column: 7, }, }, }, }, }); }); it("can export class names with _ prefix", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect((result as LoadedPCData).exports.style.classNames).to.eql({ _b: { name: "_b", scopedName: "_pub-80f4925f__b", public: true }, }); }); // Addresses https://github.com/paperclipui/paperclip/issues/319 it("shows an error if including a mixin that doesn't exist within a mixin that's exported", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 63, line: 4, column: 20 }, end: { pos: 70, line: 4, column: 27 }, }, message: "Reference not found.", }); }); // Addresses https://github.com/paperclipui/paperclip/issues/326 it("can have nested pseudo selectors", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( "" ); }); // Addresses: https://github.com/paperclipui/paperclip/issues/340 it("Can use mixins in other style blocks defined in the same page", async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql(""); }); // Addresses https://github.com/paperclipui/paperclip/issues/417 it("properly renders global * selector", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql(""); }); it("Certain declarations are auto-prefixed", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( "" ); }); it("Properly renders nested selectors", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( "" ); }); it("Can include style rules within mixins", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql(""); }); it("Can include mixin rules into a style rule", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( "" ); }); it("Can include media declarations within style rule", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( "" ); }); it("Can include a nested rule within a media rule", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( "" ); }); it("Can define a selector mixin with @content", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql(""); }); it("Can include @content with a rule", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( "" ); }); it("Can include @content within @media", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( "" ); }); it("can include a media query mixin", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( "" ); }); // Fix https://github.com/paperclipui/paperclip/issues/529 it(`can use & in media query include 1`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); // sanity after #529 it(`can use & in media query include 2`, async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `` ); }); it("properly orders include with nested selector", async () => { const graph = { "/entry.pc": ``, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( "" ); }); // fix https://github.com/paperclipui/paperclip/issues/535 it("multiple :not selectors work", async () => { const graph = { "/entry.pc": `
    I'm red
    I'm blue
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    I'm red
    I'm blue
    ` ); }); // Fixes https://github.com/paperclipui/paperclip/issues/534 it("can add extra specificty for nested elements", async () => { const graph = { "/entry.pc": `
    I'm a button
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    I'm a button
    ` ); }); // Fixes https://github.com/paperclipui/paperclip/issues/534 it("ensures that :self selectors are given higher priority", async () => { const graph = { "/entry.pc": `
    I'm a button
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    I'm a button
    ` ); }); it(":self is given higher priority than declarations", async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(":within applies styles when div is within ancestor", async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it("can nest selectors in :within", async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it("can nest group selectors in :within", async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`:within(:global()) works`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`nested & works`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`:within works as combo selector`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`:self:empty works`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`Can include @media in scoped :style`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`Can include @media in scoped :style()`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`Can include @media in :within()`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`Can use & without :self & stay ordered`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`Can use & without :self`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`Single & in scoped styled provides the same specificty`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`Can :within within :self`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`Can define :within within &`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`url vars work`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    ` ); }); it(`can load CSS files directly`, async () => { const graph = { "/entry.pc": `
    Hello world
    `, "/styles.css": ` .test { color: red; } `, }; const engine = await createMockEngine(graph); const text = stringifyLoadResult(await engine.open("/entry.pc")); expect(text).to.eql( `
    Hello world
    ` ); }); it(`breaks if ; is missing from decl`, async () => { const graph = { "/entry.pc": `
    Hello world
    `, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Graph", uri: "/entry.pc", info: { kind: "Unexpected", message: "Unexpected token", range: { start: { pos: 100, line: 6, column: 25 }, end: { pos: 100, line: 6, column: 25 }, }, }, }); }); // TODO - this is broken with CSS patcher xit(`breaks if inline declaration is defined without semicolon`, async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql("a"); }); // TODO - this is broken with CSS patcher it(`styles are sorted correctly`, async () => { const graph = { "/entry.pc": ` `, "/a.pc": ` `, "/b.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `` ); }); it(`keyframes can have multiple percentages`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`media rule can have nested media rules`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`@-webkit-keyframes works`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`can escape class names`, async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(result.exports).to.eql({ style: { kind: "Exports", classNames: { "a.b": { name: "a.b", scopedName: "_pub-80f4925f_a.b", public: true, }, "a/b": { name: "a/b", scopedName: "_pub-80f4925f_a/b", public: true, }, "a:b": { name: "a:b", scopedName: "_pub-80f4925f_a:b", public: true, }, }, mixins: {}, variables: {}, keyframes: {}, }, components: {}, }); }); it(`colon can be added on class`, async () => { const graph = { "/entry.pc": `
    `, "/test.css": ` .test\\:container { color: red; } .sm\\:p-3\\.5 { color: blue; } `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`keyframes that are exported also include a private scoped one`, async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `` ); }); it(`Can inject scopes into the document`, async () => { const graph = { "/entry.pc": `
    `, "/test.css": ` .test { color: red; } `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); // fixes https://github.com/paperclipui/paperclip/issues/644 it(`Can include mixins in mixins`, async () => { const graph = { "/entry.pc": `
    `, "/breakpoints.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`patches CSS files`, async () => { const graph = { "/entry.pc": `
    `, "/test.css": ` div { color: red; } `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); engine.updateVirtualFileContent( "/test.css", ` div { color: blue; } ` ); const result2 = engine.getLoadedData("/entry.pc"); expect(stringifyLoadResult(result2)).to.eql( `
    ` ); }); // Fixes https://github.com/paperclip-ui/paperclip/issues/970 it(`Should error if a resource isn't found`, async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err.message).to.eql(`Unable to resolve file: ./not-found.png`); }); }); ================================================ FILE: packages/paperclip-core/src/test/virtual/error-test.ts ================================================ import { expect } from "chai"; import { createMockEngine } from "../utils"; describe(__filename + "#", () => { [ [ `abba`, { errorKind: "Graph", uri: "/entry.pc", info: { kind: "Unterminated", message: "Unterminated element.", range: { start: { pos: 0, line: 1, column: 1 }, end: { pos: 3, line: 1, column: 4 } } } } ], [ `\n\n`, { errorKind: "Graph", uri: "/entry.pc", info: { kind: "Unterminated", message: "Unterminated element.", range: { start: { pos: 2, line: 3, column: 1 }, end: { pos: 5, line: 3, column: 4 } } } } ], [ `\n\ndiv { bc: 2; }
    `, 1 ], [ `works for attribute equalities`, `
    `, 0 ], [ `works for color props`, `
    `, 1 ] ].forEach(([title, source, len]: [string, string, number]) => { it(title, () => { const engine = createMockEngine({ "entry.pc": source }); try { engine.open("entry.pc"); // eslint-disable-next-line } catch (e) {} expect(engine.lint("entry.pc").length).to.eql(len); }); }); }); ================================================ FILE: packages/paperclip-core/src/test/virtual/pc-test.ts ================================================ import { expect } from "chai"; import { createMockEngine, cleanHTML, waitForError, stringifyLoadResult, noop, } from "../utils"; import { EngineDelegateEventKind, LoadedPCData, PCExports, stringifyVirtualNode, } from "@paperclip-ui/utils"; describe(__filename + "#", () => { it("prevents circular dependencies", async () => { const graph = { "/entry.pc": ``, "/module.pc": ``, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err.message).to.eql("Circular dependencies are not supported"); }); it("dynamic attributes work", async () => { const graph = { "/entry.pc": `
    {children}
    `, }; const engine = await createMockEngine(graph); const { preview } = (await engine.open("/entry.pc")) as LoadedPCData; const buffer = `${stringifyVirtualNode(preview)}`; expect(cleanHTML(buffer)).to.eql( `
    ` ); }); it("Can import keyframes", async () => { const graph = { "/entry.pc": ` `, "/module.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); const buffer = `${stringifyLoadResult(result)}`; expect(cleanHTML(buffer)).to.eql( `` ); }); it("Doesn't crash if importing module with parse error", async () => { const graph = { "/entry.pc": `
    `, "/module.pc": ` { const graph = { "/entry.pc": `
    `, "/module.pc": ` { const graph = { "/entry.pc": ` `, "/module.pc": `nothing to export!`, }; const engine = await createMockEngine(graph); let err; try { engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 58, line: 4, column: 9 }, end: { pos: 66, line: 4, column: 17 }, }, message: "Unable to find component, or it's not exported.", }); }); it("displays error if img src not found", async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph, noop, { resolveFile() { return null; }, }); let err; try { engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 19, line: 2, column: 19 }, end: { pos: 33, line: 2, column: 33 }, }, message: "Unable to resolve file: /not/found.png", }); }); describe("Slots", async () => { it("Can render attributes with element bindings", async () => { const graph = { "/entry.pc": `
    }>
    `, }; const engine = await createMockEngine(graph); const { preview } = (await engine.open("/entry.pc")) as LoadedPCData; const buffer = `${stringifyVirtualNode(preview)}`; expect(cleanHTML(buffer)).to.eql( `
    ` ); }); it(`Can render a slot with a negative number`, async () => { const graph = { "/entry.pc": `{-1}`, }; const engine = await createMockEngine(graph); const { preview } = (await engine.open("/entry.pc")) as LoadedPCData; expect(cleanHTML(stringifyVirtualNode(preview))).to.eql( `-1` ); }); }); it("Engine can't reload a file if there's an error", async () => { const graph = { "/entry.pc": ` abc `, }; const engine = await createMockEngine(graph); const result = stringifyLoadResult(await engine.open("/entry.pc")); expect(result).to.eql(` abc`); const e = waitForError(engine); engine.updateVirtualFileContent(`/entry.pc`, `
    `); const result2 = stringifyLoadResult(await engine.open("/entry.pc")); expect(result2).to.eql( `` ); }); it("Engine can't reload content if module errors", async () => { const graph = { "/entry.pc": ` abc `, "/module.pc": `
    {children} cde
    `, }; const engine = await createMockEngine(graph); const result = stringifyLoadResult(await engine.open("/entry.pc")); expect(result).to.eql( `
    abc cde
    ` ); // make the parse error await engine.updateVirtualFileContent( "/module.pc", `
    {chi` ); await engine.updateVirtualFileContent( "/entry.pc", ` defg` ); let err; // shouldn't be able to load /entry.pc now try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Graph", uri: "/module.pc", info: { kind: "Unterminated", message: "Unterminated slot.", range: { start: { pos: 41, line: 2, column: 6 }, end: { pos: 44, line: 2, column: 9 }, }, }, }); // introduce fix await engine.updateVirtualFileContent( "/module.pc", `
    cde {children}
    ` ); const result3 = stringifyLoadResult(await engine.open("/entry.pc")); expect(result3).to.eql( `
    cde defg
    ` ); }); it("Errors for incorrectly formatted slot number", async () => { const graph = { "/entry.pc": ` {10.10.10} `, }; const engine = await createMockEngine(graph); let err; try { engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 10, line: 2, column: 10 }, end: { pos: 18, line: 2, column: 18 }, }, message: "Invalid number.", }); }); it("Entities are encoded", async () => { const graph = { "/entry.pc": ` × `, }; const engine = await createMockEngine(graph); expect(stringifyLoadResult(await engine.open("/entry.pc"))).to.eql( " ×" ); }); it("Returns component properties", async () => { const graph = { "/entry.pc": `
    {d} {e?} {f?}
    `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as LoadedPCData; expect(result.exports.components).to.eql({ Test: { name: "Test", properties: { c: { name: "c", optional: false, }, e: { name: "e", optional: true, }, a: { name: "a", optional: true, }, b: { name: "b", optional: false, }, f: { name: "f", optional: false, }, d: { name: "d", optional: false, }, }, public: false, }, Test2: { name: "Test2", properties: {}, public: true, }, }); }); it("Exports are updated", async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as LoadedPCData; expect(result.exports.components).to.eql({ Test: { name: "Test", properties: {}, public: false, }, }); await engine.updateVirtualFileContent( "/entry.pc", `
    ` ); const result2 = engine.getLoadedData("/entry.pc"); expect((result2.exports as PCExports).components).to.eql({ Test2: { name: "Test2", properties: {}, public: false, }, }); }); it("Conditional blocks are collected in export props", async () => { const graph = { "/entry.pc": `
    {a && b? && (c) && {d}}
    `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as LoadedPCData; expect(result.exports.components).to.eql({ Test: { name: "Test", properties: { a: { name: "a", optional: false, }, b: { name: "b", optional: true, }, c: { name: "c", optional: false, }, d: { name: "d", optional: false, }, }, public: false, }, }); }); it("Cannot declare a component twice", async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 58, line: 4, column: 9 }, end: { pos: 105, line: 5, column: 15 }, }, message: "Component name is already declared.", }); }); it("Throws an error if an imported module has a CSS error", async () => { const graph = { "/entry.pc": ` `, "/module.pc": ` `, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Graph", uri: "/entry.pc", info: { kind: "Unterminated", message: "Unterminated element.", range: { start: { pos: 9, line: 2, column: 9 }, end: { pos: 34, line: 2, column: 34 }, }, }, }); }); it("Displays an error if open tag is unclosed", async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); engine.getLoadedAst("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Graph", uri: "/entry.pc", info: { kind: "Unexpected", message: "Unexpected token", range: { start: { pos: 14, line: 2, column: 14 }, end: { pos: 14, line: 2, column: 14 }, }, }, }); }); // we don't want this since it is _implicit_ xit("Can apply a class to {class?} without needing $", async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const buffer = stringifyLoadResult(await engine.open("/entry.pc")); expect(buffer).to.eql( `
    ` ); }); xit(`Can apply a class to className={className?} without needing $`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const buffer = stringifyLoadResult(await engine.open("/entry.pc")); expect(buffer).to.eql( `
    ` ); }); xit(`Can apply a class to className="a {className?}" without needing $`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const buffer = stringifyLoadResult(await engine.open("/entry.pc")); expect(buffer).to.eql( `
    ` ); }); it(`Doesn't apply scope if $ is provided`, async () => { const graph = { "/entry.pc": ` `, "/module.pc": `
    `, }; const engine = await createMockEngine(graph); const buffer = stringifyLoadResult(await engine.open("/entry.pc")); expect(buffer).to.eql( `
    ` ); }); // addresses https://github.com/paperclipui/paperclip/issues/336 it(`Dynamic styles are ommitted if their associated prop is undefined`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const buffer = stringifyLoadResult(await engine.open("/entry.pc")); expect(buffer).to.eql( `
    ` ); }); // addresses https://github.com/paperclipui/paperclip/issues/362 it(`Can have class names with underscores in them`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const buffer = stringifyLoadResult(await engine.open("/entry.pc")); expect(buffer).to.eql( `
    ` ); }); // Addresses https://github.com/paperclipui/paperclip/issues/299 it(`Errors if component is not defined at the root`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); engine.getLoadedAst("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 25, line: 3, column: 11 }, end: { pos: 67, line: 4, column: 17 }, }, message: "Components need to be defined at the root.", }); }); it(`Errors if component defined in element within a slot`, async () => { const graph = { "/entry.pc": `
    }>
    `, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); engine.getLoadedAst("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 20, line: 2, column: 20 }, end: { pos: 48, line: 2, column: 48 }, }, message: "Components need to be defined at the root.", }); }); // Addresses https://github.com/paperclipui/paperclip/issues/372 it(`Displays an error if a shadow pierce import is missing`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 20, line: 2, column: 20 }, end: { pos: 29, line: 2, column: 29 }, }, message: "Reference not found.", }); }); // addresses: https://github.com/paperclipui/paperclip/issues/389 it(`Displays an error if a class name is not found for shadow pierce`, async () => { const graph = { "/entry.pc": `
    `, "/module.pc": ` `, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 69, line: 3, column: 24 }, end: { pos: 78, line: 3, column: 33 }, }, message: "Class name not found.", }); }); it(`Display an error if class name is private for shadow pierce`, async () => { const graph = { "/entry.pc": `
    `, "/module.pc": ` `, }; const engine = await createMockEngine(graph); let err; try { await engine.open("/entry.pc"); } catch (e) { err = e; } expect(err).to.eql({ errorKind: "Runtime", uri: "/entry.pc", range: { start: { pos: 69, line: 3, column: 24 }, end: { pos: 78, line: 3, column: 33 }, }, message: "This class reference is private.", }); }); it(`Can use a public class pierce`, async () => { const graph = { "/entry.pc": `
    `, "/module.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Deprecated >>> works`, async () => { const graph = { "/entry.pc": `
    `, "/module.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Can use a public class pierce on a component`, async () => { const graph = { "/entry.pc": `
    `, "/module.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Can use a component that's referencing a public class`, async () => { const graph = { "/entry.pc": ` `, "/module.pc": `
    `, "/module2.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Prefixes classnames if they come after shadow pierce`, async () => { const graph = { "/entry.pc": `
    `, "/module.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Can change the tag name of an element`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Cannot change tag name if not exposed`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Does not render undefined if child isn't present`, async () => { const graph = { "/entry.pc": `
    {children} {slot}
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Doesn't crash when dependency is updated`, async () => { const graph = { "/entry.pc": ` `, "/a.pc": `a`, "/b.pc": `
    b
    `, }; let crashErr; const engine = await createMockEngine(graph, (e) => { crashErr = e; }); await engine.open("/entry.pc"); await engine.open("/b.pc"); try { // write a syntax error await engine.updateVirtualFileContent( "/b.pc", `
    b
    ` ); // eslint-disable-next-line } catch (e) {} await engine.updateVirtualFileContent("/a.pc", `aa`); expect(crashErr).to.eql(undefined); await engine.updateVirtualFileContent( "/b.pc", `
    bb
    ` ); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    bb
    ` ); }); it(`Can define nested style blocks`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Can define nested slot style blocks`, async () => { const graph = { "/entry.pc": `
    {test}
    abba } /> `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    abba
    ` ); }); it(`Can define scoped styles without :self selector`, async () => { const graph = { "/entry.pc": `
    `, "/path.png": "", }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Can include mixins within scoped styles`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Can include mixins within scoped styles with a decl`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Can define style tag for void elements`, async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `` ); }); it(`Can scope media queries`, async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `` ); }); it(`Can apply styles to a component`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Can apply styles to a component that has a class already defined`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Can apply styles to instance of instance of component`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`Properly emits correct events`, async () => { const graph = { "/entry.pc": ` a `, }; const engine = await createMockEngine(graph); const events = []; engine.onEvent(events.push.bind(events)); await engine.open("/entry.pc"); await engine.updateVirtualFileContent("/entry.pc", "b"); await engine.updateVirtualFileContent("/entry.pc", "c"); expect(events.map((event) => event.kind)).to.eql([ EngineDelegateEventKind.Loaded, EngineDelegateEventKind.Evaluated, EngineDelegateEventKind.Diffed, EngineDelegateEventKind.Diffed, ]); }); // fixes https://github.com/paperclipui/paperclip/issues/508 it(`properly applies scoped style for nested & combo`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`can use :self with group selectors`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it("properly scopes nested selectors in :self", async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( `
    ` ); }); it(`properly chains nested combo selector`, async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); const result = await engine.open("/entry.pc"); expect(stringifyLoadResult(result)).to.eql( "" ); }); it(`last node is removed if it's whitespace`, async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; expect(result.preview.children[0].kind).to.eql("Element"); expect(result.preview.children.length).to.eql(1); }); // Dunno why I added this - empty frames should just be ignored. If someone // wants a frame to be visible, they can just add content xit(`last node is preserved if it has annotations`, async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; expect(result.preview.children.length).to.eql(2); expect(result.preview.children[1].value).to.eql("\n "); }); it(`scoped styles can be applied to component instances`, async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; const buffer = `${stringifyLoadResult(result)}`; expect(buffer).to.eql( `` ); }); it(`Works with logical && and nodes`, async () => { const graph = { "/entry.pc": ` {0 &&
    A
    } {true &&
    B
    } {1 &&
    C
    } {false &&
    D
    } {false &&
    D
    ||
    E
    } {false &&
    D
    || false || 99 \n\n} {false &&
    D
    || false || 0 && "blah" } {false ||
    F
    } `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; const buffer = `${stringifyLoadResult(result)}`; expect(buffer).to.eql( `0
    B
    C
    E
    990
    F
    ` ); }); it(`Works with ! negation`, async () => { const graph = { "/entry.pc": ` {!false} {!!false} {!!!false} {!!!0} {!0 &&
    A
    } `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; const buffer = `${stringifyLoadResult(result)}`; expect(buffer).to.eql( `truetruetrue
    A
    ` ); }); it(`Can parse groups ()`, async () => { const graph = { "/entry.pc": ` {(true || true) &&
    Something
    } `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; const buffer = `${stringifyLoadResult(result)}`; expect(buffer).to.eql( `
    Something
    ` ); }); it(`Can have various tag names`, async () => { const graph = { "/entry.pc": ` <_a_2 /> <$a /> `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; const buffer = `${stringifyLoadResult(result)}`; expect(buffer).to.eql( `<_a_2 class="_80f4925f _pub-80f4925f"><$a class="_80f4925f _pub-80f4925f">` ); }); it(`Includes style from element defined within conditional block`, async () => { const graph = { "/entry.pc": `
    {a && } {b}
    }/> `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; const buffer = `${stringifyLoadResult(result)}`; expect(buffer).to.eql( `
    ` ); }); xit(`Treats class & className the same`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; const buffer = `${stringifyLoadResult(result)}`; expect(buffer).to.eql( `
    ` ); }); it(`Can delete files from the graph`, async () => { const graph = { "/entry.pc": ` a `, "/mod.pc": `
    {children} b
    `, }; const engine = await createMockEngine(graph); let result = (await engine.open("/entry.pc")) as any; expect(stringifyLoadResult(result)).to.eql( `
    a b
    ` ); graph["/mod.pc"] = undefined; engine.purgeUnlinkedFiles(); try { await engine.open("/entry.pc"); } catch (e) { expect(e.info.message).to.eql("import not found"); } await engine.updateVirtualFileContent("/entry.pc", "a"); result = (await engine.open("/entry.pc")) as any; expect(stringifyLoadResult(result)).to.eql( `a` ); }); // https://github.com/paperclipui/paperclip/issues/708 it(`Re-evaluates module after error & no change`, async () => { const graph = { "/entry.pc": `
    `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; let lastEvent; engine.onEvent((e) => { lastEvent = e; }); expect(stringifyLoadResult(result)).to.eql( `
    ` ); await engine.updateVirtualFileContent("/entry.pc", "
    "); expect(lastEvent).to.eql({ kind: "Diffed", uri: "/entry.pc", data: { kind: "PC", sheetMutations: [], allImportedSheetUris: [], dependencies: {}, exports: { style: { kind: "Exports", classNames: {}, mixins: {}, variables: {}, keyframes: {}, }, components: {}, }, mutations: [], }, }); }); it(`Can inject an imported namespace into a class`, async () => { const graph = { "/entry.pc": `
    `, "/atoms.pc": ` `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; const buffer = `${stringifyLoadResult(result)}`; expect(buffer).to.eql( `
    ` ); }); it(`Properly inject styles`, async () => { const graph = { "/entry.pc": `
    `, "/a.pc": ` `, "/b.pc": ` `, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; const buffer = `${stringifyLoadResult(result)}`; expect(buffer).to.eql( `
    ` ); }); // https://github.com/paperclip-ui/paperclip/issues/970 it(`Errors if image src is not found`, async () => { const graph = { "/entry.pc": ` `, }; const engine = await createMockEngine(graph); let err; try { (await engine.open("/entry.pc")) as any; } catch (e) { err = e; } expect(err).not.eql(undefined); expect(err.message).to.eql(`Unable to resolve file: /blah.png`); }); it(`Keeps imports sorted as they came in`, async () => { const graph = { // imp.pc has different sorting in BTreeMap "/entry.pc": `
    `, "/imp.pc": ``, "/imp2.pc": ``, }; const engine = await createMockEngine(graph); const result = (await engine.open("/entry.pc")) as any; const buffer = `${stringifyLoadResult(result)}`; expect(buffer).to.eql( `
    ` ); }); }); ================================================ FILE: packages/paperclip-core/src/test/virtual/raws-test.ts ================================================ import { expect } from "chai"; import { isNode, isRule, isMaybeStyleSheet, isStyleDeclaration, isStyleObject, KeyframeRule, StyleRule, isStyleSelector, NodeKind, RuleKind, SelectorKind, StyleDeclarationKind, AnnotationPropertyKind } from "@paperclip-ui/utils"; import { createMockEngine } from "../utils"; describe(__filename + "#", () => { [ `
    `, `
    a
    `, `
    a
    b `, `
    b `, `
    b `, ` `, `
    ` ].forEach(source => { it(`Maintains the whitepsace for ${source.replace( /[\s\r\n\t]+/g, " " )}`, async () => { const graph = { "/entry.pc": source }; const engine = await createMockEngine(graph); await engine.open("/entry.pc"); const ast = (await engine.getLoadedAst("/entry.pc")) as any; const output = stringifyAST(ast); // ends may be chopped off (because of text nodes), so // only test inner conten expect(output.trim()).to.eql(source.trim()); }); }); }); const stringifyAST = ast => { if (isNode(ast)) { switch (ast.nodeKind) { case NodeKind.Fragment: { return ast.children.map(stringifyAST).join(""); } case NodeKind.Element: { const buffer = [ast.raws.before]; buffer.push(`<${ast.tagName}`); if (ast.attributes.length) { const attrs = ast.attributes.map(stringifyAST); buffer.push(` `, attrs.join(" ")); } buffer.push(">"); buffer.push(ast.children.map(stringifyAST).join("")); buffer.push(``); return buffer.join(""); } case NodeKind.Text: { return ast.value; } case NodeKind.Comment: { const buffer = [ast.raws.before, ``, ast.raws.after); return buffer.join(""); } case NodeKind.StyleElement: { const buffer = [ast.raws?.before]; buffer.push(""); buffer.push(ast.raws?.after || ""); return buffer.join(""); } case NodeKind.Slot: { return ""; } } } else if (isStyleObject(ast)) { if (isRule(ast)) { switch (ast.ruleKind) { case RuleKind.Style: { return stringifyStyleRule(ast); } case RuleKind.Mixin: { const buffer = [ast.raws.before, "@mixin ", ast.name.value, " {"]; buffer.push(...ast.declarations.map(stringifyAST)); buffer.push(...ast.rules.map(stringifyStyleRule)); buffer.push("}", ast.raws.after); return buffer.join(""); } case RuleKind.FontFace: { const buffer = [ast.raws.before, "@font-face {"]; buffer.push(...ast.declarations.map(stringifyAST)); buffer.push("}", ast.raws.after); return buffer.join(""); } case RuleKind.Keyframes: { const buffer = [ast.raws.before, "@keyframes ", ast.name, " {"]; buffer.push(...ast.rules.map(stringifyKeyframe)); buffer.push("}", ast.raws.after); return buffer.join(""); } } } else if (isStyleSelector(ast)) { // NOTE: only need to pick one since raws aren't kept in selectors switch (ast.selectorKind) { case SelectorKind.Element: { return ast.tagName; } } } else if (isStyleDeclaration(ast)) { switch (ast.declarationKind) { case StyleDeclarationKind.KeyValue: { return `${ast.raws.before}${ast.name}: ${ast.value};${ast.raws.after}`; } case StyleDeclarationKind.Content: { return `${ast.raws.before}@content;${ast.raws.after}`; } case StyleDeclarationKind.Include: { const buffer = [ ast.raws.before, "@include ", ast.mixinName.parts.map(part => part.name).join("."), " {" ]; buffer.push(...ast.declarations.map(stringifyAST)); buffer.push(...ast.rules.map(stringifyStyleRule)); buffer.push("}", ast.raws.after); return buffer.join(""); } case StyleDeclarationKind.Media: { const buffer = [ast.raws.before, "@media", ast.conditionText, "{"]; buffer.push(...ast.declarations.map(stringifyAST)); buffer.push(...ast.rules.map(stringifyStyleRule)); buffer.push("}", ast.raws.after); return buffer.join(""); } } } else if (isMaybeStyleSheet(ast)) { const buffer = [ast.raws.before]; buffer.push(...ast.declarations.map(stringifyAST)); buffer.push(...ast.rules.map(stringifyAST)); buffer.push(ast.raws.after); return buffer.join(""); } } }; const stringifyStyleRule = (ast: StyleRule) => { const buffer = [ast.raws.before, stringifyAST(ast.selector), " {"]; buffer.push(...ast.declarations.map(stringifyAST)); buffer.push(...ast.children.map(stringifyStyleRule)); buffer.push("}", ast.raws.after); return buffer.join(""); }; const stringifyKeyframe = (ast: KeyframeRule) => { const buffer = [ast.raws.before, ast.key, "{"]; buffer.push(...ast.declarations.map(stringifyAST)); buffer.push("}", ast.raws.after); return buffer.join(""); }; ================================================ FILE: packages/paperclip-core/test-fixtures/.vscode/settings.json ================================================ {} ================================================ FILE: packages/paperclip-core/test-fixtures/modules/@nested/in/a/folder/package.json ================================================ { "name": "folder", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } ================================================ FILE: packages/paperclip-core/test-fixtures/modules/@nested/in/a/folder/paperclip.config.json ================================================ { "compilerOptions": { "outDir": "./lib" }, "srcDir": "./src", "moduleDirs": ["modules"] } ================================================ FILE: packages/paperclip-core/test-fixtures/modules/@nested/in/a/folder/src/component.pc ================================================
    Some Module
    something like that ================================================ FILE: packages/paperclip-core/test-fixtures/modules/@nested/in/a/folder/src/imp-mod-a.pc ================================================ ================================================ FILE: packages/paperclip-core/test-fixtures/modules/@nested/in/a/folder/src/module.pc ================================================ something ================================================ FILE: packages/paperclip-core/test-fixtures/modules/@nested/in/a/folder/src/test.pc ================================================
    from test.pc
    fsdfs fdsfsd ================================================ FILE: packages/paperclip-core/test-fixtures/modules/module-a/package.json ================================================ { "name": "module-a", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } ================================================ FILE: packages/paperclip-core/test-fixtures/modules/module-a/paperclip.config.json ================================================ { "compilerOptions": {}, "srcDir": "./src" } ================================================ FILE: packages/paperclip-core/test-fixtures/modules/module-a/src/component.pc ================================================
    Some Module
    something like that ================================================ FILE: packages/paperclip-core/test-fixtures/modules/module-a/src/module.pc ================================================ something
    okay
    ================================================ FILE: packages/paperclip-core/test-fixtures/modules/module-a/src/test.pc ================================================
    from test.pc
    fsdfs fdsfsd ================================================ FILE: packages/paperclip-core/test-fixtures/paperclip.config.json ================================================ { "srcDir": "./src", "moduleDirs": ["./modules"] } ================================================ FILE: packages/paperclip-core/test-fixtures/src/bad-css-url.pc ================================================ ================================================ FILE: packages/paperclip-core/test-fixtures/src/bad-import.pc ================================================ ================================================ FILE: packages/paperclip-core/test-fixtures/src/good-import.pc ================================================ ================================================ FILE: packages/paperclip-core/test-fixtures/src/hello-world.pc ================================================ Hello World ================================================ FILE: packages/paperclip-core/test-fixtures/src/mod-a-import.pc ================================================ ================================================ FILE: packages/paperclip-core/test-fixtures/src/mod-import.pc ================================================ ================================================ FILE: packages/paperclip-core/test-fixtures/src/module.pc ================================================
    I'm a secret!
    ================================================ FILE: packages/paperclip-core/test-fixtures/src/nested-mod-import.pc ================================================ ================================================ FILE: packages/paperclip-core/tsconfig.esm.json ================================================ { "compilerOptions": { "outDir": "esm", "target": "es6", "moduleResolution": "node", "allowSyntheticDefaultImports": true }, "exclude": [ "node_modules", "*-test", "lib", "index.d.ts", "browser.d.ts", "test", "examples", "*.json" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-core/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./lib", "target": "es5", "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true, "strict": false /* Enable all strict type-checking options. */ }, "exclude": [ "**/*.d.ts", "lib/**/*.d.ts", "esm", "native", "lib/**/*", "node_modules", "*-test", "index.d.ts", "browser.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-coverage/package.json ================================================ { "name": "@paperclip-ui/coverage", "version": "18.5.6", "description": "", "main": "lib/index.js", "types": "lib/index.ds.ts", "publishConfig": { "access": "public" }, "scripts": { "build": "yarn build:paperclip && tsc", "build:paperclip": "paperclip-minimal build", "build:watch": "yarn build:paperclip && concurrently \"tsc --watch\" \"yarn build:paperclip --watch\" " }, "author": "", "license": "ISC", "dependencies": { "@paperclip-ui/utils": "^18.5.6", "chalk": "2.0.0", "cli-table": "^0.3.11", "html-entities": "^1.2.1", "typescript": "^4.2.3" }, "devDependencies": { "@paperclip-ui/cli-minimal": "^18.5.6", "@paperclip-ui/compiler-html": "^18.5.6", "concurrently": "^6.5.1" } } ================================================ FILE: packages/paperclip-coverage/paperclip.config.json ================================================ { "compilerOptions": [ { "target": "html", "outDir": "lib", "mainCSSFileName": "main.css", "assetOutDir": "lib/assets", "es5": true, "emit": ["js", "js.map", "css"] }, { "target": "html", "emit": ["d.ts"] } ], "srcDir": "./src" } ================================================ FILE: packages/paperclip-coverage/src/constants.ts ================================================ export const OK_THRESHOLD = 90; export const POOR_THRESHOLD = 60; ================================================ FILE: packages/paperclip-coverage/src/generate/html/index.ts ================================================ import { CoverageReport } from "@paperclip-ui/utils"; import { StringRange } from "@paperclip-ui/utils"; import * as fsa from "fs-extra"; import * as path from "path"; import * as reportStyles from "./ui/report.pc"; import * as pageStyles from "./ui/page.pc"; import { kebabCase } from "lodash"; import { Html5Entities } from "html-entities"; import { convertReportToTree, TreeDirectoryReport, TreeFileReport, TreeReport, TreeReportKind } from "../../state"; import { OK_THRESHOLD, POOR_THRESHOLD } from "../../constants"; const entities = new Html5Entities(); export type GenerateCoverageHTML = { output: string; cwd: string; }; export const writeCoverageHTML = ( report: CoverageReport, options: GenerateCoverageHTML ) => { const files = generateCoverageHTML(report, options); for (const filePath in files) { fsa.mkdirpSync(path.dirname(filePath)); fsa.writeFileSync(filePath, files[filePath]); } return files; }; const generateCoverageHTML = ( report: CoverageReport, options: GenerateCoverageHTML ) => { const tree = convertReportToTree(report, options.cwd); const files = generateReportPages(tree, options); files[path.join(options.output, "main.css")] = fsa.readFileSync( __dirname + "/../../assets/main.css", "utf-8" ); return files; }; const generateReportPages = ( tree: TreeReport, options: GenerateCoverageHTML, pages: Record = {} ) => { pages[path.join(options.output, getHTMLPathName(tree.path))] = generateReport( tree, options ); if (tree.kind === TreeReportKind.Directory) { for (const child in tree.children) { generateReportPages(tree.children[child], options, pages); } } return pages; }; const generateReport = (report: TreeReport, options: GenerateCoverageHTML) => { if (report.kind === TreeReportKind.Directory) { return generateDirectoryReport(report, options); } else { return generateFileReport(report, options); } }; const getHTMLPathName = (path: string[]) => path.map(kebabCase).join("/") + "/index.html"; const generateCoverageOuter = ( report: TreeReport, options: GenerateCoverageHTML, children: string ) => { const htmlPerc = perc(report.html.count / report.html.total); const cssPerc = perc(report.css.count / report.css.total); const linesPerc = perc(report.lines.count / report.lines.total); return pageStyles.default({ head: generateHead(report, options), children: reportStyles.Report({ title: "Coverage Report", ...colorCodeProps(Math.min(linesPerc, htmlPerc, cssPerc)), coverage: [ reportStyles.CoverageInfo({ title: "HTML", percentage: htmlPerc, coveredCount: report.html.count, totalCount: report.html.total }), reportStyles.CoverageInfo({ title: "CSS", percentage: cssPerc, coveredCount: report.css.count, totalCount: report.css.total }), reportStyles.CoverageInfo({ title: "Lines", percentage: linesPerc, coveredCount: report.lines.count, totalCount: report.lines.total }) ], breadcrumbs: reportStyles.Breadcrumbs({ children: (report.path.length ? [path.basename(options.output), ...report.path] : [] ) .map((part, i, ary) => { let href; const length = ary.length - i - 1; if (length) { href = Array.from({ length }) .map(v => "..") .join("/") + "/index.html"; } return reportStyles.Breadcrumb({ href, children: part }); }) .join("\n") }), children }) }); }; const generateDirectoryReport = ( report: TreeDirectoryReport, options: GenerateCoverageHTML ) => { return generateCoverageOuter( report, options, reportStyles.Table({ children: [ reportStyles.Row({ header: true, children: [ reportStyles.Cell({ children: "file" }), reportStyles.Cell({ children: "HTML" }), reportStyles.Cell({ children: "CSS" }), reportStyles.Cell({ children: "Lines" }) ] }), ...Object.entries(report.children).map(([key, child]) => { const cssPerc = perc(child.css.count / child.css.total); const htmlPerc = perc(child.html.count / child.html.total); const linePerc = perc(child.lines.count / child.lines.total); const filePerc = Math.min(cssPerc, htmlPerc, linePerc); const relativePath = [child.path[child.path.length - 1]]; return reportStyles.Row({ children: [ reportStyles.FileCell({ href: getHTMLPathName(relativePath), ...colorCodeProps(htmlPerc), children: relativePath.join("/"), percentage: filePerc }), reportStyles.PercentCell({ percentage: htmlPerc, ...colorCodeProps(htmlPerc), coveredCount: child.html.count, totalCount: child.html.total }), reportStyles.PercentCell({ percentage: cssPerc, ...colorCodeProps(cssPerc), coveredCount: child.css.count, totalCount: child.css.total }), reportStyles.PercentCell({ percentage: linePerc, ...colorCodeProps(linePerc), coveredCount: child.lines.count, totalCount: child.lines.total }) ] }); }) ] }) ); }; const perc = (value: number) => { const v = Math.floor(value * 100); return isNaN(v) ? 100 : v; }; const colorCodeProps = (percentage: number) => { const props = { okay: false, poor: false, good: false }; if (percentage < POOR_THRESHOLD) { props.poor = true; } else if (percentage < OK_THRESHOLD) { props.okay = true; } else { props.good = true; } return props; }; const generateFileReport = ( report: TreeFileReport, options: GenerateCoverageHTML ) => { return generateCoverageOuter( report, options, reportStyles.FileReportContent({ children: report.content.split("\n").map((line, i) => { const poor = lineIsInRange(i + 1, [ ...report.report.html.missingRanges, ...report.report.css.missingRanges ]); return reportStyles.Line({ number: i + 1, poor, good: !poor, children: entities.encode(line).replace(/\s/g, "  ") + "
    " }); }) }) ); }; const lineIsInRange = (i, ranges: StringRange[]) => { for (const range of ranges) { if (i >= range.start.line && i <= range.end.line) { return true; } } return false; }; const generateHead = (report: TreeReport, options: GenerateCoverageHTML) => { return ` `; }; ================================================ FILE: packages/paperclip-coverage/src/generate/html/ui/atoms.pc ================================================ ================================================ FILE: packages/paperclip-coverage/src/generate/html/ui/file-report.pc ================================================ ================================================ FILE: packages/paperclip-coverage/src/generate/html/ui/page.pc ================================================ {head} {children} ================================================ FILE: packages/paperclip-coverage/src/generate/html/ui/report.pc ================================================
    {children}
    {href && {children} || children}
    {children}
    {children}
    {children}
    {children}
    {title}
    {percentage}% ({coveredCount} / {totalCount})

    {title}

    {breadcrumbs}
    {coverage}
    {children}
    {children}
    } breadcrumbs={ path to directory }> I'm code I'm code I'm code I'm code I'm code I'm code I'm code
    {children}
    {number} {children}
    } breadcrumbs={ path to directory }> file HTML CSS Lines path/to/file.pc path/to/file.pc path/to/file.pc path/to/file.pc path/to/file.pc
    ================================================ FILE: packages/paperclip-coverage/src/generate/stdout/index.ts ================================================ import * as Table from "cli-table"; import * as path from "path"; import * as URL from "url"; const chalk = require("chalk"); import { CoverageReport } from "@paperclip-ui/utils"; import { convertReportToTree } from "../../state"; import { OK_THRESHOLD, POOR_THRESHOLD } from "../../constants"; export const printCoverage = (report: CoverageReport, cwd: string) => { const tree = convertReportToTree(report, cwd); const table = new Table({ head: ["File", "HTML", "CSS", "Lines"], colWidths: [80, 10, 10, 10], style: { head: ["bold", "cyan"] } }); const totals = { lines: [0, 0], css: [0, 0], html: [0, 0] }; for (const file of report.files) { const linePerc = calcPerc(file.missingLines.length, file.lineCount); const cssPerc = calcPerc(file.css.missingRanges.length, file.css.count); const htmlPerc = calcPerc(file.html.missingRanges.length, file.html.count); totals.css[0] += file.css.missingRanges.length; totals.css[1] += file.css.count; totals.html[0] += file.html.missingRanges.length; totals.html[1] += file.html.count; totals.lines[0] += file.missingLines.length; totals.lines[1] += file.lineCount; table.push([ path.relative(cwd, URL.fileURLToPath(file.uri)), perc(htmlPerc), perc(cssPerc), perc(linePerc) ]); } table.push([ chalk.bold.cyan("Summary"), perc(calcPerc(totals.html[0], totals.html[1])), perc(calcPerc(totals.css[0], totals.css[1])), perc(calcPerc(totals.lines[0], totals.lines[1])) ]); console.log(table.toString()); }; const calcPerc = (missing: number, total: number) => { if (total === 0) { return 100; } return Math.floor(((total - missing) / total) * 100); }; const perc = (value: number) => { let str = `${value}%`; if (value < POOR_THRESHOLD) { str = chalk.red(str); } else if (value < OK_THRESHOLD) { str = chalk.yellow(str); } else { str = chalk.green(str); } return str; }; ================================================ FILE: packages/paperclip-coverage/src/index.ts ================================================ export * from "./generate/html"; export * from "./generate/stdout"; ================================================ FILE: packages/paperclip-coverage/src/state.ts ================================================ import { CoverageReport, FileReport } from "@paperclip-ui/utils"; import * as fs from "fs"; import * as URL from "url"; export enum TreeReportKind { Directory, File } export type CoverageInfo = { total: number; count: number; }; type BaseReport = { kind: TKind; path: string[]; css: CoverageInfo; html: CoverageInfo; lines: CoverageInfo; }; export type TreeDirectoryReport = { children: Record; } & BaseReport; export type TreeFileReport = { report: FileReport; content: string; } & BaseReport; export type TreeReport = TreeFileReport | TreeDirectoryReport; export const convertReportToTree = (report: CoverageReport, cwd: string) => { const root: TreeDirectoryReport = { kind: TreeReportKind.Directory, path: [], css: { count: 0, total: 0 }, html: { count: 0, total: 0 }, lines: { count: 0, total: 0 }, children: {} }; for (const file of report.files) { const filePath = URL.fileURLToPath(file.uri); const relativePath = filePath.replace(cwd, ""); const path = relativePath.split("/").filter(Boolean); let current: TreeDirectoryReport = root; for (let i = 0, { length } = path; i < length - 1; i++) { const part = path[i]; current = (current.children[part] || (current.children[part] = createDirectoryReport( path.slice(0, i + 1) ))) as TreeDirectoryReport; } current.children[path[path.length - 1]] = createFileReport( path, file, fs.readFileSync(filePath, "utf-8") ); } return generateSummary(root); }; const generateSummary = (tree: TreeReport): TreeReport => { let base = { ...tree, css: calcCoverage(flattenReports(tree).map(report => report.css)), html: calcCoverage(flattenReports(tree).map(report => report.html)), lines: calcCoverage(flattenReports(tree).map(report => report.lines)) }; if (base.kind === TreeReportKind.Directory) { base = { ...base, children: Object.entries(base.children).reduce((dict, [part, child]) => { dict[part] = generateSummary(child); return dict; }, {}) }; } return base; }; const flattenReports = (tree: TreeReport): TreeFileReport[] => { if (tree.kind === TreeReportKind.Directory) { const files: TreeFileReport[] = []; for (const part in tree.children) { const report = tree.children[part]; if (report.kind === TreeReportKind.File) { files.push(report); } else { files.push(...flattenReports(report)); } } return files; } else { return [tree]; } }; const calcCoverage = (coverages: CoverageInfo[]) => { return { count: coverages.reduce((sum, v) => sum + v.count, 0), total: coverages.reduce((sum, v) => sum + v.total, 0) }; }; const createDirectoryReport = (path: string[]): TreeDirectoryReport => ({ kind: TreeReportKind.Directory, path, css: { count: 0, total: 0 }, html: { count: 0, total: 0 }, lines: { count: 0, total: 0 }, children: {} }); const createFileReport = ( path: string[], report: FileReport, content: string ): TreeFileReport => ({ kind: TreeReportKind.File, path, report, css: { total: report.css.count, count: report.css.count - report.css.missingRanges.length }, html: { total: report.html.count, count: report.html.count - report.html.missingRanges.length }, lines: { total: report.lineCount, count: report.lineCount - report.missingLines.length }, content }); ================================================ FILE: packages/paperclip-coverage/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./lib", "target": "es5", "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true, "declaration": true, "strict": false /* Enable all strict type-checking options. */ }, "exclude": [ "**/*.d.ts", "lib/**/*.d.ts", "esm", "native", "lib/**/*", "node_modules", "*-test", "index.d.ts", "browser.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-diff/package.json ================================================ { "name": "@paperclip-ui/diff", "version": "18.5.6", "description": "", "main": "lib/index.js", "scripts": { "build": "yarn build:paperclip && tsc", "build:paperclip": "paperclip-minimal build", "build:watch": "concurrently \"tsc --watch\" \"yarn build:paperclip --watch\"" }, "publishConfig": { "access": "public" }, "author": "", "license": "ISC", "dependencies": { "@paperclip-ui/core": "^18.5.6", "@paperclip-ui/diff-utils": "^18.5.6", "@paperclip-ui/utils": "^18.5.6", "chalk": "^4.0.0", "execa": "^6.0.0", "fs-extra": "^10.0.0", "glob": "^7.2.0", "html-entities": "^1.2.1", "immer": "^9.0.7", "mime-types": "^2.1.34", "ora": "5.0.0", "p-limit": "^2.0.0", "pixelmatch": "^5.2.1", "pngjs": "^6.0.0", "puppeteer": "^13.0.0", "sift": "^15.1.3", "simple-git": "^2.48.0" }, "devDependencies": { "@paperclip-ui/cli-minimal": "^18.5.6", "@paperclip-ui/compiler-html": "^18.5.6" } } ================================================ FILE: packages/paperclip-diff/paperclip.config.json ================================================ { "compilerOptions": [ { "target": "html", "outDir": "lib", "assetOutDir": "lib/assets", "mainCSSFileName": "main.css", "es5": true }, { "target": "html", "emit": ["d.ts"] } ], "srcDir": "./src" } ================================================ FILE: packages/paperclip-diff/src/cli.ts ================================================ import * as sn from "./snapshots"; import { writeReport } from "./report"; import { Provider, start } from "./core"; import { PaperclipResourceWatcher } from "@paperclip-ui/core"; import { logInfo } from "./utils"; export type SnapshotOptions = { cwd: string; force?: boolean; }; export const snapshot = async (options: SnapshotOptions) => { const provider = await start(options.cwd); await sn.snapshot(provider)(options.force); await provider.close(); }; export type DetectChangesOptions = { cwd: string; branch: string; html?: boolean; output?: string; open?: boolean; watch?: boolean; }; export const detectChanges = async (options: DetectChangesOptions) => { const provider = await start(options.cwd); const run = async (open?: boolean) => { await detectChanges2({ ...options, open }, provider); if (options.watch) { logInfo(`Waiting for file changes...`); const watcher = new PaperclipResourceWatcher(".", options.cwd); watcher.onChange(() => { watcher.dispose(); run(false); }); } }; await run(options.open); if (!options.watch) { await provider.close(); } }; const detectChanges2 = async ( options: DetectChangesOptions, provider: Provider ) => { const result = await sn.detectChanges(provider)(options.branch); await writeReport( { ...result, gitDir: provider.gitDir, cwd: options.cwd, open: options.open }, options ); }; ================================================ FILE: packages/paperclip-diff/src/core.ts ================================================ import { PaperclipResourceWatcher } from "@paperclip-ui/core"; import { resolvePCConfig } from "@paperclip-ui/utils"; import { produce } from "immer"; const PNG = require("pngjs").PNG; import * as pupetter from "puppeteer"; import git, { SimpleGit } from "simple-git"; const pixelmatch = require("pixelmatch"); export type Provider = { browser: pupetter.Browser; git: SimpleGit; cwd: string; gitDir: string; close: () => void; }; /** */ export const start = async (cwd: string): Promise => { const browser = await pupetter.launch(); const _git = git(cwd); return { browser, git: _git, cwd, gitDir: await _git.revparse([`--show-toplevel`]), close() { browser.close(); } }; }; ================================================ FILE: packages/paperclip-diff/src/index.ts ================================================ export * from "./report"; export * from "./cli"; ================================================ FILE: packages/paperclip-diff/src/report/html/index.ts ================================================ import * as path from "path"; import * as fsa from "fs-extra"; import { DetectChangesResult, snapshot } from "../../snapshots"; import { exec } from "child_process"; import { FrameScreenshot, FrameScreenshotDiff, FrameSnapshot, getFrameScreenshots, getProjectFrames, getProjectScreenshots, getProjectSnapshot, getScreenshotDiffs, getScreenshotPath } from "../../state"; import * as pageStyles from "./page.pc"; import * as styles from "./report.pc"; import { logInfo } from "../../utils"; import { Html5Entities } from "html-entities"; const entities = new Html5Entities(); const DEFAULT_OUTPUT_DIR = ".paperclip/diff/report"; const MAIN_CSS_PATH = path.join(__dirname, "../../assets/main.css"); export type WriteHTMLReportOptions = { gitDir: string; cwd: string; output?: string; open?: boolean; } & DetectChangesResult; export const writeHTMLReport = async (info: WriteHTMLReportOptions) => { const files = generateHTMLReport(info); const baseDir = getBaseDir(info); logInfo(`Writing HTML report...`); const otherScreenshotIds = getProjectScreenshots( info.deltaVersion, info.manifest ).map(diff => diff.id); for (const screenshot of getProjectScreenshots( info.currentVersion, info.manifest )) { fsa.cpSync( getScreenshotPath(info.gitDir, screenshot.id), path.join(baseDir, screenshot.id + ".png") ); const diff = getScreenshotDiffs(screenshot, info.manifest).find(diff => otherScreenshotIds.includes(diff.otherScreenshotId) ); if (diff) { fsa.cpSync( getScreenshotPath(info.gitDir, diff.id), path.join(baseDir, diff.id + ".png") ); fsa.cpSync( getScreenshotPath(info.gitDir, diff.otherScreenshotId), path.join(baseDir, diff.otherScreenshotId + ".png") ); } } for (const fileName in files) { const filePath = path.join(baseDir, fileName); fsa.writeFileSync(filePath, files[fileName]); } logInfo( `HTML report written to ./${path.relative(info.cwd, baseDir)}/index.html` ); if (info.open) { exec(`open ${baseDir}/index.html`); } }; const generateHTMLReport = (info: WriteHTMLReportOptions) => { const files: Record = {}; const frames = getProjectFrames( getProjectSnapshot(info.currentVersion, info.manifest), info.manifest ); for (const frame of frames) { files[frame.id + ".html"] = generateSnapshotPage(frame, info); } const sidebar = generateSidebarProps(info); // TODO - need to create summary page. For now we use alias to first page if (sidebar.length) { files["index.html"] = files[sidebar[0].id + ".html"]; } files["main.css"] = fsa.readFileSync(MAIN_CSS_PATH, "utf-8"); return files; }; const getBaseDir = (info: WriteHTMLReportOptions) => path.join( info.cwd, info.output || DEFAULT_OUTPUT_DIR, info.currentVersion, info.deltaVersion ); const generateSnapshotPage = ( snapshot: FrameSnapshot, info: WriteHTMLReportOptions ) => { const screenshots = getFrameScreenshots(snapshot, info.manifest); const sourceFile = entities.encode(snapshot.sourceFilePath); const filePath = path.join(info.gitDir, snapshot.sourceFilePath); return pageStyles.default({ head: ``, children: styles.Report({ title: entities.encode(snapshot.title), subtitle: `${sourceFile}`, sidebar: generateSidebar(snapshot, info), content: screenshots .map(screenshot => { return generateComparison(screenshot, info); }) .join("\n") }) }); }; const generateComparison = ( screenshot: FrameScreenshot, info: WriteHTMLReportOptions ) => { const otherScreenshotIds = getProjectScreenshots( info.deltaVersion, info.manifest ).map(diff => diff.id); const diff = getScreenshotDiffs(screenshot, info.manifest).find(diff => otherScreenshotIds.includes(diff.otherScreenshotId) ); return [ styles.Comparison({ children: [ styles.Screenshot({ a: true, title: `A: ${info.deltaVersion}`, children: diff ? `` : `No screenshot found` }), styles.Screenshot({ b: true, title: `B: ${info.currentVersion}`, children: `` }) ] }), diff && styles.Screenshot({ c: true, title: "Changes", children: `` }), styles.Divider({}) ].join("\n"); }; const generateSidebarProps = ( info: WriteHTMLReportOptions, activeSnapshot?: FrameSnapshot ) => { const project = getProjectSnapshot(info.currentVersion, info.manifest); const frames = getProjectFrames(project, info.manifest); const allProps = frames.map(frame => { const screenshots = getFrameScreenshots(frame, info.manifest); const diffs: FrameScreenshotDiff[] = screenshots.reduce( (diffs, screenshot) => { return [...diffs, ...getScreenshotDiffs(screenshot, info.manifest)]; }, [] ); const hasChanges = diffs.some(diff => diff.changeCount > 0); return { href: "./" + frame.id + ".html" + "#" + frame.id, id: frame.id, title: entities.encode(frame.title), src: "./" + screenshots[0].id + ".png", hasChanges, active: frame.id === activeSnapshot?.id, noChanges: !hasChanges }; }); allProps.sort((a, b) => { return a.hasChanges ? -1 : 1; }); return allProps; }; const generateSidebar = ( activeSnapshot: FrameSnapshot, info: WriteHTMLReportOptions ) => { const allProps = generateSidebarProps(info, activeSnapshot); return allProps.map(props => styles.Thumbnail(props)); }; ================================================ FILE: packages/paperclip-diff/src/report/html/page.pc ================================================ {head} {children} ================================================ FILE: packages/paperclip-diff/src/report/html/report.pc ================================================
    {title || "Untitled"}
    {children}
    {title}
    {children}
    {sidebar}

    {title}

    {subtitle}
    {content}

    path/to/file.pc} sidebar={ } content={ something B DIFF A B DIFF } /> ================================================ FILE: packages/paperclip-diff/src/report/index.ts ================================================ import { DetectChangesResult } from "../snapshots"; import { writeHTMLReport, WriteHTMLReportOptions } from "./html"; import { writeConsoleReport } from "./stdout"; export type WriteChangesOptions = { html?: boolean; }; export const writeReport = async ( changes: WriteHTMLReportOptions, options: WriteChangesOptions ) => { await writeConsoleReport(changes); if (options.html) { await writeHTMLReport(changes); } }; ================================================ FILE: packages/paperclip-diff/src/report/stdout/index.ts ================================================ const chalk = require("chalk"); import { DetectChangesResult } from "../../snapshots"; import { getChanges, getDiffFrame } from "../../state"; import { logWarn } from "../../utils"; export const writeConsoleReport = async ({ currentVersion, deltaVersion, manifest }: DetectChangesResult) => { const changes = getChanges(currentVersion, deltaVersion, manifest); for (const change of changes) { if (change.changeCount) { const frame = getDiffFrame(change, manifest); logWarn( `Change detected in ${chalk.bold( `${frame.sourceFilePath} : ${frame.title}` )}` ); } } }; ================================================ FILE: packages/paperclip-diff/src/snapshots.ts ================================================ import * as path from "path"; const chalk = require("chalk"); const pLimit = require("p-limit"); const crypto = require("crypto"); import * as URL from "url"; import { eachFrame } from "@paperclip-ui/diff-utils"; import * as fsa from "fs-extra"; import { logInfo, logWarn } from "./utils"; import * as ora from "ora"; import * as mime from "mime-types"; import { addDiffToManifest, addFrameSnapshots, DATA_DIR, FrameScreenshot, FrameScreenshotDiff, FrameSnapshot, getScreenshotPath, Manifest, MANIFEST_FILE_NAME, MANIFEST_VERSION, ProjectSnapshot, screenshotEquals, SCREENSHOTS_DIR, WindowSize } from "./state"; const PNG = require("pngjs").PNG; import { Provider } from "./core"; const pixelmatch = require("pixelmatch"); // TODO - need to pull from config const WINDOW_SIZES: WindowSize[] = [{ width: 1400, height: 768 }]; /** * Generates new snapshot from current branch */ export const snapshot = (provider: Provider) => async (force?: boolean) => { const status = await provider.git.status(); if (status.files.length && !force) { return logWarn( `You need to commit your file changes before creating a snapshot. You may add ${chalk.bold( "--force" )} to ignore this warning.` ); } const latestCommit = (await provider.git.log()).latest.hash; const currentBranch = await (await provider.git.branch()).current; const spinner = ora( `Generating base ${chalk.bold(currentBranch)} snapshots...` ).start(); await saveScreenshots(provider)(latestCommit, currentBranch); spinner.stop(); }; export type DetectChangesResult = { manifest: Manifest; currentVersion?: string; deltaVersion?: string; }; export const detectChanges = (provider: Provider) => async ( branch?: string ): Promise => { const { git, cwd, gitDir } = provider; const currentBranch = await (await git.branch()).current; const deltaBranch = branch || currentBranch; let manifest = getManifest(gitDir); const deltaCommit = manifest.commitAliases[deltaBranch]; let spinner = ora( `Generating latest ${chalk.bold(currentBranch)} snapshots...` ).start(); const version = currentBranch; manifest = await saveScreenshots(provider)(version, version + "@latest"); await spinner.stop(); await timeout(500); logInfo(`Diffing against commit sha ${chalk.bold(deltaCommit)}...`); const snapshot = manifest.projectSnapshots.find( snapshot => snapshot.version === version ); const deltaSnapshot = manifest.projectSnapshots.find( snapshot => snapshot.version === deltaCommit ); // return { // manifest, // currentVersion: snapshot.version, // deltaVersion: deltaSnapshot.version // }; const snapshotPairs = getSnapshotPairs(snapshot, deltaSnapshot, manifest); for (const hash in snapshotPairs) { const screenshotPairs = snapshotPairs[hash]; for (const [aid, bid] of screenshotPairs) { // it's a new frame if (!bid) { continue; } const apath = getScreenshotPath(gitDir, aid); const bpath = getScreenshotPath(gitDir, bid); const info = await diffImages(apath, bpath); const diff: FrameScreenshotDiff = { id: md5(aid + bid), changeCount: info.changeCount, screnshotId: aid, otherScreenshotId: bid }; await saveDiffScreenshot(gitDir, diff.id, info); manifest = addDiffToManifest(aid, diff, manifest); } } saveManigest(gitDir, manifest); return { manifest, currentVersion: snapshot.version, deltaVersion: deltaSnapshot.version }; }; const getSnapshotPairs = ( a: ProjectSnapshot, b: ProjectSnapshot, manifest: Manifest ) => { const framePairs = {}; const aframes = manifest.frameSnapshots.filter(v => a.frameIds.includes(v.id) ); const bframes = manifest.frameSnapshots.filter(v => b.frameIds.includes(v.id) ); for (const aframe of aframes) { const bframe = bframes.find(frame => frame.hash === aframe.hash); const screenshotPairs: Array = []; const ascreenshots = manifest.screenshots.filter(screenshot => aframe.screenshotIds.includes(screenshot.id) ); for (const ascreenshot of ascreenshots) { const pairIds: string[] = [ascreenshot.id]; const bscreenshot = bframe && manifest.screenshots.find( screenshot => bframe.screenshotIds.includes(screenshot.id) && screenshotEquals(ascreenshot, screenshot) ); if (bscreenshot) { pairIds.push(bscreenshot.id); } screenshotPairs.push(pairIds); } framePairs[aframe.hash] = screenshotPairs; } return framePairs; }; /** */ /** */ const saveDiffScreenshot = (gitDir: string, id: string, { diff }) => { const filePath = getScreenshotPath(gitDir, id); fsa.writeFileSync(filePath, PNG.sync.write(diff)); }; /** */ const timeout = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); /** */ const getManifestPath = (cwd: string) => path.join(cwd, DATA_DIR, MANIFEST_FILE_NAME); export const getManifest = (gitDir: string): Manifest => { const filePath = getManifestPath(gitDir); if (fsa.existsSync(filePath)) { const manifest = JSON.parse(fsa.readFileSync(filePath, "utf-8")); // just dump it if (manifest.version === MANIFEST_VERSION) { return manifest; } } return { version: MANIFEST_VERSION, commitAliases: {}, frameSnapshots: [], screenshots: [], diffs: [], projectSnapshots: [] }; }; /** */ const saveScreenshots = ({ gitDir, cwd, browser, git }: Provider) => async ( version: string, tag?: string ) => { await fsa.mkdirp(getScreenshotsDir(gitDir)); const limit = pLimit(10); const allFrameSnapshots: FrameSnapshot[] = []; const allScreenshots: FrameScreenshot[] = []; const resolveAsset = (filePath: string) => { // console.log(`data:${mime.lookup(filePath)};base64,${fsa.readFileSync(filePath).toString("base64")}`); return `data:${mime.lookup(filePath)};base64,${fsa .readFileSync(filePath) .toString("base64")}`; }; await eachFrame( ".", { cwd, resolveAsset }, async ({ id: frameHash, html, title, filePath }) => { return limit(async () => { const browserVersion = await browser.version(); const screenshotIds: string[] = []; for (const size of WINDOW_SIZES) { const hash = md5( frameHash + browserVersion + size.width + size.height ); const id = md5(version + hash); const filePath = getScreenshotPath(gitDir, id); const page = await browser.newPage(); page.setViewport(size); await page.setContent(html); // stop text input focus await page.focus("body"); // wait for content to load await page.waitForNetworkIdle(); // TODO - wait for transition end await page.screenshot({ path: filePath }); screenshotIds.push(id); allScreenshots.push({ id, size, browser: browserVersion, diffIds: [] }); } allFrameSnapshots.push({ id: md5(version + frameHash), hash: frameHash, title, sourceFilePath: path.relative(gitDir, filePath), screenshotIds }); }); } ); const manifest = await addFrameSnapshots( { version, frameIds: allFrameSnapshots.map(snapshot => snapshot.id) }, allFrameSnapshots, allScreenshots, gitDir, version, tag, getManifest(gitDir) ); saveManigest(gitDir, manifest); return manifest; }; /** */ const diffImages = (snapshotFilePath: string, deltaSnapshotPath: string) => { const img1 = PNG.sync.read(fsa.readFileSync(snapshotFilePath)); const img2 = PNG.sync.read(fsa.readFileSync(deltaSnapshotPath)); const { width, height } = img1; const diff = new PNG({ width, height }); const changeCount = pixelmatch( img1.data, img2.data, diff.data, width, height, { threshold: 0.2 } ); return { diff, changeCount }; }; /** */ export const saveManigest = (gitDir: string, manifest: Manifest) => { const filePath = getManifestPath(gitDir); fsa.writeFileSync(filePath, JSON.stringify(manifest, null, 2)); }; /** */ const getScreenshotsDir = (gitDir: string) => path.join(gitDir, SCREENSHOTS_DIR); const md5 = (value: string) => { return crypto .createHash("md5") .update(value) .digest("hex"); }; ================================================ FILE: packages/paperclip-diff/src/state.ts ================================================ export const MANIFEST_VERSION = 7; import { produce } from "immer"; import * as path from "path"; export const MANIFEST_FILE_NAME = "manifest.json"; export const DIFF_BOUNDARY = "~"; export const PC_HIDDEN_DIR = ".paperclip"; export const DIFF_DIR = PC_HIDDEN_DIR + "/diff"; export const DATA_DIR = DIFF_DIR + "/data"; export const SCREENSHOTS_DIR = DATA_DIR + "/screenshots"; export type WindowSize = { width: number; height: number; }; export type FrameScreenshotDiff = { id: string; changeCount: number; screnshotId: string; otherScreenshotId: string; }; export type FrameScreenshot = { id: string; browser: string; size: WindowSize; diffIds: string[]; }; export type FrameSnapshot = { id: string; // may be explicitly defined by user hash: string; title: string; sourceFilePath: string; screenshotIds: string[]; }; export type ProjectSnapshot = { version: string; frameIds: string[]; }; export type Manifest = { version: number; diffs: FrameScreenshotDiff[]; screenshots: FrameScreenshot[]; frameSnapshots: FrameSnapshot[]; projectSnapshots: ProjectSnapshot[]; commitAliases: Record; }; export type ExpandedFrameScreenshot = { diffs: FrameScreenshotDiff[]; } & FrameScreenshot; export type ExpandedFrameSnapshot = { screenshots: ExpandedFrameScreenshot[]; } & FrameSnapshot; export type ExpandedProjectSnapshot = { frames: ExpandedFrameSnapshot[]; } & ProjectSnapshot; export const screenshotEquals = (a: FrameScreenshot, b: FrameScreenshot) => a.browser === b.browser && sizeEquals(a.size, b.size); export const sizeEquals = (a: WindowSize, b: WindowSize) => a.width === b.width && a.height === b.height; export const getProjectSnapshot = (version: string, manifest: Manifest) => manifest.projectSnapshots.find(snapshot => snapshot.version === version); export const getProjectFrames = ( project: ProjectSnapshot, manifest: Manifest ) => manifest.frameSnapshots.filter(snapshot => project.frameIds.includes(snapshot.id) ); export const getFrameScreenshots = (frame: FrameSnapshot, manifest: Manifest) => manifest.screenshots.filter(screenshot => frame.screenshotIds.includes(screenshot.id) ); export const getScreenshotDiffs = ( screenshot: FrameScreenshot, manifest: Manifest ) => manifest.diffs.filter(diff => screenshot.diffIds.includes(diff.id)); export const getProjectScreenshots = (version: string, manifest: Manifest) => { const project = getProjectSnapshot(version, manifest); const screenshots: FrameScreenshot[] = []; const frames = getProjectFrames(project, manifest); for (const frame of frames) { screenshots.push(...getFrameScreenshots(frame, manifest)); } return screenshots; }; const getDiffScreenshot = (diff: FrameScreenshotDiff, manifest: Manifest) => { return manifest.screenshots.find(screenshot => screenshot.diffIds.includes(diff.id) ); }; export const getDiffFrame = (diff: FrameScreenshotDiff, manifest: Manifest) => { const diffScreenshot = getDiffScreenshot(diff, manifest); return manifest.frameSnapshots.find(frame => frame.screenshotIds.includes(diffScreenshot.id) ); }; export const getProjectDiffs = (version: string, manifest: Manifest) => { const screenshots = getProjectScreenshots(version, manifest); const diffs: FrameScreenshotDiff[] = []; for (const screenshot of screenshots) { diffs.push(...getScreenshotDiffs(screenshot, manifest)); } return diffs; }; export const getChanges = ( version1: string, version2: string, manifest: Manifest ): FrameScreenshotDiff[] => { const otherScreenshotIds = getProjectScreenshots(version2, manifest).map( v => v.id ); const v1Diffs = getProjectDiffs(version1, manifest); return v1Diffs.filter(diff => { return otherScreenshotIds.includes(diff.otherScreenshotId); }); }; export const addDiffToManifest = ( screenshotId: string, screenshotDiff: FrameScreenshotDiff, manifest: Manifest ): Manifest => { manifest = produce(manifest, newManifest => { newManifest.diffs = insert("id", [screenshotDiff], manifest.diffs); for (const screenshot of newManifest.screenshots) { if (screenshot.id === screenshotId) { if (!screenshot.diffIds.includes(screenshotDiff.id)) { screenshot.diffIds.push(screenshotDiff.id); } break; } } }); return manifest; }; export const addFrameSnapshots = async ( projectSnapshot: ProjectSnapshot, frames: FrameSnapshot[], screenshots: FrameScreenshot[], gitDir: string, version: string, tagName: string, manifest: Manifest ): Promise => { manifest = produce(manifest, newManifest => { newManifest.commitAliases[tagName] = version; newManifest.projectSnapshots = insert( "version", [projectSnapshot], newManifest.projectSnapshots ); newManifest.frameSnapshots = insert( "id", frames, newManifest.frameSnapshots ); newManifest.screenshots = insert( "id", screenshots, newManifest.screenshots ); }); return manifest; }; const insert = ( key: string, newItems: TData[], collection: TData[] ): TData[] => [ ...collection.filter( item => !newItems.some(aitem => aitem[key] === item[key]) ), ...newItems ]; export const getScreenshotPath = (gitDir: string, id: string) => path.join(gitDir, SCREENSHOTS_DIR, id + ".png"); ================================================ FILE: packages/paperclip-diff/src/utils.ts ================================================ import * as chalk from "chalk"; import * as fsa from "fs-extra"; import * as path from "path"; import { Manifest } from "./state"; const PNG = require("pngjs").PNG; export const MANIFEST_FILE_NAME = "manifest.json"; export const DIFF_BOUNDARY = "~"; export const PC_HIDDEN_DIR = ".paperclip"; export const logWarn = (message: string) => { console.warn(chalk.yellow(`[warn]`) + " " + message); }; export const logInfo = (message: string) => { console.warn(chalk.blue(`[info]`) + " " + message); }; export const logError = (message: string) => { console.warn(chalk.red(`[erro] `) + " " + message); }; ================================================ FILE: packages/paperclip-diff/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./lib", "target": "es6", "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true, "declaration": true, "strict": false /* Enable all strict type-checking options. */ }, "exclude": [ "**/*.d.ts", "lib/**/*.d.ts", "esm", "native", "lib/**/*", "node_modules", "*-test", "index.d.ts", "browser.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-diff-utils/bin/paperclip-diff ================================================ #!/usr/bin/env node const { PaperclipDiff } = require("../lib"); const diff = new PaperclipDiff(process.cwd()); const path = require("path"); const resolve = require("resolve"); const chokidar = require("chokidar"); const {init, devStart} = require("../lib/cli"); const {build} = require("@paperclip-ui/cli-minimal"); const argv = require("yargs") .command("snapshot", "Take snapshots of the current PR", yargs => { // yargs // .option("watch", desc("watch for changes")) // .option("print", desc("Only print compiled output")) // .option("only", desc("Target files to generate")) // .option("silent", desc("silence output information")) // .option("output", desc("output directory of files")) // .option("compiler", desc("Language compiler target (default is from paperclip.config.json if found)")) }, argv => { build({ cwd: process.cwd(), config: argv.config, print: argv.print, output: argv.output, css: argv.css, watch: argv.watch, definition: argv.definition, only: argv.only && argv.only.split(",").map(v => v.trim()), verbose: !argv.silent, dropPcExtension: argv.dropPcExtension, compilerName: argv.compiler, filePatterns: argv.filePattern }) }) .command("changes [branch]", "Show changes against a different branch", yargs => { yargs .option("watch", desc("re-run when a PC file changes")); // .option("print", desc("Only print compiled output")) // .option("only", desc("Target files to generate")) // .option("silent", desc("silence output information")) // .option("output", desc("output directory of files")) // .option("compiler", desc("Language compiler target (default is from paperclip.config.json if found)")) }, argv => { diff.detectChanges(argv.branch, argv.watch); }) ================================================ FILE: packages/paperclip-diff-utils/package.json ================================================ { "name": "@paperclip-ui/diff-utils", "version": "18.5.6", "description": "", "main": "lib/index.js", "publishConfig": { "access": "public" }, "scripts": { "build": "tsc", "build:watch": "tsc --watch" }, "author": "", "license": "ISC", "dependencies": { "@paperclip-ui/cli-utils": "^18.5.6", "@paperclip-ui/core": "^18.5.6", "@paperclip-ui/utils": "^18.5.6", "glob": "^7.2.0", "globby": "^11.0.4" } } ================================================ FILE: packages/paperclip-diff-utils/src/index.ts ================================================ export * from "./utils"; export * from "./pc-document"; ================================================ FILE: packages/paperclip-diff-utils/src/pc-document.ts ================================================ import * as fs from "fs"; import * as url from "url"; import { VirtualFragment, stringifyVirtualNode } from "@paperclip-ui/core"; export const getPCDocumentHTML = (root: VirtualFragment) => { return `${stringifyVirtualNode( root )}`; }; export const getDocumenAssetPaths = (source: string) => { const re = `([\\('"])(file://.*?)([\\)'"])`; const global = new RegExp(re, "g"); const local = new RegExp(re); const matches = source.match(global) || []; const uris = []; for (const match of matches) { const [, , uri] = match.match(local); // shouldn't happen, but just in case try { const pathname = url.fileURLToPath(uri); if (fs.existsSync(pathname)) { uris.push(pathname); } } catch (e) { console.error(e); } } return uris; }; export const embedAssets = ( source: string, serializeFilePath: (value: string) => string ) => { let newSource = source; const re = `([\\('"])(file://.*?)([\\)'"])`; const global = new RegExp(re, "g"); const local = new RegExp(re); const matches = source.match(global) || []; const assetPaths = getDocumenAssetPaths(source); for (const assetPath of assetPaths) { const newUrl = serializeFilePath(assetPath); newSource = newSource.replace(url.pathToFileURL(assetPath).href, newUrl); } return newSource; }; ================================================ FILE: packages/paperclip-diff-utils/src/utils.ts ================================================ // Inspiration: https://github.com/percy/cli/blob/43a608c1f49e0e65cc78e00a55a9506c45173da5/packages/cli-upload/src/commands/upload.js // https://github.com/percy/cli/blob/43a608c1f49e0e65cc78e00a55a9506c45173da5/packages/cli-upload/src/resources.js const globby = require("globby"); import * as path from "path"; import * as fs from "fs"; import * as url from "url"; import { createEngineDelegate, VirtualFragment, VirtualNodeKind, paperclipSourceGlobPattern, VirtualStyleElement, EngineMode, LoadedData, VirtualFrame, computeVirtScriptObject, NodeAnnotations, EvaluatedDataKind, } from "@paperclip-ui/core"; import { embedAssets, getPCDocumentHTML } from "./pc-document"; import { getPrettyMessageFromError } from "@paperclip-ui/cli-utils"; import * as crypto from "crypto"; export type RunOptions = { cwd: string; keepEmpty?: boolean; snapshotNameTemplate?: string; resolveAsset?: (filePath: string) => string; }; const EMPTY_CONTENT_STATE = ``; export type EachFrameInfo = { id: string; filePath: string; html: string; annotations: NodeAnnotations; title: string; uniqueTitle: string; assets: Record; }; const defaultFilter = (filePaths: string[]) => Promise.resolve(filePaths); export const eachFrame = async ( sourceDirectory: string, { cwd = process.cwd(), keepEmpty, snapshotNameTemplate = "{frameFilePath}: {frameTitle}", resolveAsset, }: Partial = {}, each: (info: EachFrameInfo) => Promise ) => { let paperclipFilePaths = await globby( paperclipSourceGlobPattern(sourceDirectory), { cwd, absolute: true, gitignore: true, ignore: ["**/node_modules/**"], followSymbolicLinks: true, } ); const engine = await createEngineDelegate({ mode: EngineMode.MultiFrame, }); const promises: any = []; for (const filePath of paperclipFilePaths) { const relativePath = path.relative(cwd, filePath); let result: LoadedData; const uri = url.pathToFileURL(filePath).href; try { result = await engine.open(uri); } catch (e) { const inf = getPrettyMessageFromError( e, fs.readFileSync(filePath, "utf-8"), uri, cwd ); console.error(inf || e); continue; } if (result.kind !== EvaluatedDataKind.PC) { continue; } const { sheet, importedSheets, preview } = result; const frames = ( preview.kind === VirtualNodeKind.Fragment ? preview.children : [preview] ) as VirtualFrame[]; const used: Record = {}; for (let i = 0, { length } = frames; i < length; i++) { const frame = frames[i]; const annotations: NodeAnnotations = (frame.annotations && computeVirtScriptObject(frame.annotations)) || {}; const root: VirtualFragment = { sourceId: null, id: null, children: [ ...importedSheets.map(({ sheet }) => createStyle(sheet)), createStyle(sheet), frame, ], kind: VirtualNodeKind.Fragment, }; let frameLabel = annotations.frame?.title || `Untitled`; let uniqueFramelabel = frameLabel; if (used[frameLabel]) { uniqueFramelabel = frameLabel + " " + used[frameLabel]; used[frameLabel]++; } else { used[frameLabel] = 1; } const html = getPCDocumentHTML(root); const isDocEmpty = !keepEmpty && isEmpty(html); const shouldSkip = annotations.visualRegresionTest === false; const data = { frameFilePath: relativePath, frameTitle: uniqueFramelabel, }; const snapshotName = snapshotNameTemplate.replace( /\{(.*?)\}/g, (cap, propName) => { return data[propName]; } ); if (shouldSkip) { console.info(`skip ${snapshotName}`); continue; } const assetPaths: Record = {}; const fixedHTML = embedAssets( html, resolveAsset || ((filePath) => { return (assetPaths[filePath] = "/" + encodeURIComponent(filePath)); }) ); promises.push( each({ html: fixedHTML, annotations, title: uniqueFramelabel, uniqueTitle: snapshotName, assets: assetPaths, filePath, id: md5(snapshotName), }) ); } await Promise.all(promises); } }; const isEmpty = (source: string) => { return ( source.replace(/[\r\n\s\t]+/g, "").replace(/` }, }); } } return null; }; const getSourceNodeFromPath = ( uri: string, engine: EngineDelegate, path: string ) => engine.getVirtualNodeSourceInfo(path ? path.split(".").map(Number) : [], uri); const deleteNode = ( uri: string, engine: EngineDelegate, edit: DeleteNode ): DocumentTextEdit[] => { const nodePath = edit.nodePath.split(".").map(Number); const info = getSourceNodeFromPath(uri, engine, edit.nodePath); const [sourceUri, expr] = engine.getExpressionById(info.sourceId); const ownerExpr = getASTAncestors( expr, engine.getLoadedAst(uri) as DependencyNodeContent ).find((expr) => isAttribute(expr) || isNode(expr)); if (ownerExpr && isAttribute(ownerExpr)) { return [ { uri: sourceUri, chars: [], index: ownerExpr.range.start.pos, deleteCount: ownerExpr.range.end.pos - ownerExpr.range.start.pos, }, ]; } const edits: DocumentTextEdit[] = [ { uri: info.textSource.uri, chars: [], index: info.textSource.range.start.pos, deleteCount: info.textSource.range.end.pos - info.textSource.range.start.pos, }, ]; const virtTarget = getVirtTarget( (engine.getLoadedData(uri) as LoadedPCData).preview, nodePath ) as VirtualElement | VirtualText; // Delete annotations if present if (virtTarget.annotations) { const [annotationsUri, annotationsSource] = engine.getExpressionById( virtTarget.annotations.sourceId ) as [string, Comment]; edits.push({ uri: annotationsUri, chars: [], index: annotationsSource.range.start.pos, deleteCount: annotationsSource.range.end.pos - annotationsSource.range.start.pos, }); } return edits; }; const getNodeIndentation = ( uri: string, engine: EngineDelegate, nodePath: string ) => { const info = getSourceNodeFromPath(uri, engine, nodePath); const [exprUri, expr] = engine.getExpressionById(info.sourceId) as [ string, Element ]; return getExprIndentation(exprUri, engine, expr); }; const getExprIndentation = ( uri: string, engine: EngineDelegate, expr: Expression ) => { const content = engine.getVirtualContent(uri); const lines = content.split("\n"); return lines[expr.range.start.line - 1].match(/^[\s\t]*/)[0]; }; const insertNodeBefore = ( uri: string, documents: DocumentManager, engine: EngineDelegate, edit: InsertNodeBefore ) => { const [exprUri, expr] = getEditTarget( { kind: EditTargetKind.VirtualNode, nodePath: edit.beforeNodePath }, uri, engine ); const parentExpr = getASTParent( expr, engine.getLoadedAst(uri) as DependencyNodeContent ) as Fragment | Element; const [child, additionalEdit] = getChildInsertionContent( edit.node, documents, exprUri, engine, false ); const trailing = parentExpr.range.end.line === parentExpr.range.start.line ? "" : "\n"; const chars = addMultiLineIndentation( child, getExprIndentation(exprUri, engine, expr) ).split(""); if ( parentExpr.children.findIndex((child) => child.id === expr.id) === 0 && parentExpr.nodeKind === NodeKind.Element ) { return { uri: exprUri, chars: ["\n", ...chars], index: parentExpr.tagNameRange.end.pos + 1, }; } return { uri: exprUri, chars: [...chars, trailing], index: expr.range.start.pos, }; }; const prependChild = ( uri: string, documents: DocumentManager, engine: EngineDelegate, edit: PrependChild ) => { const [exprUri, expr] = getEditTarget(edit.target, uri, engine) as [ string, Node ]; const [child] = getChildInsertionContent( edit.child, documents, exprUri, engine, false ); if (expr.nodeKind === NodeKind.Element) { if (expr.children.length === 0) { const root = (engine.getLoadedData(exprUri) as LoadedPCData).preview; const virtNode = getVirtNodeBySourceId(root, expr.id); return appendChild(uri, documents, engine, { kind: VirtualObjectEditKind.AppendChild, nodePath: getNodePath(virtNode, root), child: { value: child }, }); } else { const root = (engine.getLoadedData(exprUri) as LoadedPCData).preview; const virtNode = getVirtNodeBySourceId(root, expr.children[0].id); return insertNodeBefore(uri, documents, engine, { kind: VirtualObjectEditKind.InsertNodeBefore, beforeNodePath: getNodePath(virtNode, root), node: { value: child }, }); } } return { uri: exprUri, chars: child.split(""), index: 0, }; }; const getEditTarget = ( target: EditTarget, uri: string, engine: EngineDelegate ) => { switch (target.kind) { case EditTargetKind.Expression: { return engine.getExpressionById(target.sourceId); } case EditTargetKind.VirtualNode: { const info = getSourceNodeFromPath(uri, engine, target.nodePath); return engine.getExpressionById(info.sourceId) as [string, Element]; } } }; const addAttribute = ( uri: string, engine: EngineDelegate, edit: AddAttribute ) => { const [sourceUri, expr] = getEditTarget(edit.target, uri, engine); const buffer = [edit.name]; if (edit.value) { buffer.push(`=`, edit.value); } return { uri: sourceUri, chars: (" " + buffer.join("")).split(""), index: (expr as Element).tagNameRange.end.pos, }; }; const updateAttribute = ( uri: string, engine: EngineDelegate, edit: UpdateAttribute ) => { const info = getSourceNodeFromPath(uri, engine, edit.nodePath); const [sourceUri, expr] = engine.getExpressionById(info.sourceId) as [ string, Element ]; // const attr = expr.attributes.find(attr => { // if (attr.attrKind === AttributeKind.KeyValueAttribute) { // return attr.name === edit.name; // } else if (attr.attrKind === AttributeKind.ShorthandAttribute) { // return attr.reference.scriptKind === ScriptExpressionKind.Reference // ? attr.reference.path[0].name === edit.name // : null; // } // }); const buffer = [edit.name]; if (edit.value) { buffer.push(`=`, edit.value); } return { uri: sourceUri, chars: (" " + buffer.join("")).split(""), index: expr.tagNameRange.end.pos, }; }; const setAnnotations = ( uri: string, engine: EngineDelegate, edit: SetAnnotations ) => { const parentPath = edit.nodePath.split("."); parentPath.pop(); const buffer = [`"); const virtualElement = getNodeByPath( edit.nodePath, (engine.getLoadedData(uri) as LoadedPCData).preview ) as VirtualElement; if (virtualElement.annotations) { const [uri, expr] = engine.getExpressionById( virtualElement.annotations.sourceId ) as [string, ScriptObject]; return { uri, chars: buffer.join("").split(""), index: expr.range.start.pos, deleteCount: expr.range.end.pos - expr.range.start.pos, }; } else { const info = getSourceNodeFromPath(uri, engine, edit.nodePath); const [sourceUri] = engine.getExpressionById(info.sourceId); return { uri: sourceUri, chars: [...buffer, "\n"].join("").split(""), index: info.textSource.range.start.pos, }; } }; const appendChild = ( uri: string, documents: DocumentManager, engine: EngineDelegate, edit: AppendChild ): DocumentTextEdit | DocumentTextEdit[] => { const [exprUri, expr] = getEditTarget( { kind: EditTargetKind.VirtualNode, nodePath: edit.nodePath }, uri, engine ); if (isNode(expr)) { if (expr.nodeKind === NodeKind.Element) { return appendElement(uri, documents, engine, expr, exprUri, edit); } else if (expr.nodeKind === NodeKind.Fragment) { return appendRoot(expr, documents, engine, exprUri, edit); } } else if (isScriptExpression(expr)) { if (expr.scriptKind === ScriptExpressionKind.Reference) { return appendSlot(documents, uri, engine, expr, edit); } } throw new Error(`Unknown expr`); }; const appendRoot = ( expr: Fragment, documents: DocumentManager, engine: EngineDelegate, exprUri: string, edit: AppendChild ) => { const [child] = getChildInsertionContent( edit.child, documents, exprUri, engine, false ); return { uri: exprUri, chars: child.split(""), index: expr.range.end.pos, }; }; const appendSlot = ( documents: DocumentManager, uri: string, engine: EngineDelegate, expr: Reference, edit: AppendChild ) => { const doc = documents.open(uri) as PCDocument; const path = edit.nodePath.split("."); path.pop(); const exprName = expr.path[0].name; let instanceElement: VirtualElement; let instancePath: string; for (let i = path.length; i > 0; i--) { const ancestorPath = path.slice(0, i).join("."); const ancestor = getNodeByPath( ancestorPath, doc.getContent().preview ) as VirtualElement; if (ancestor.sourceInfo?.instanceOf) { instanceElement = ancestor; instancePath = ancestorPath; break; } } // there should always be an instance element. If not, then there's a bug if (!instanceElement) { throw new Error(`Instance element not found`); } const [child, additionalEdits] = getChildInsertionContent( edit.child, documents, uri, engine, true ); if (exprName === "children") { return appendChild(uri, documents, engine, { kind: VirtualObjectEditKind.AppendChild, nodePath: instancePath, child: edit.child, }); } return [ updateAttribute(uri, engine, { kind: VirtualObjectEditKind.UpdateAttribute, nodePath: instancePath, name: exprName, value: child, }), ...additionalEdits, ]; }; const deleteAttr = (uri: string, expr: Expression): DocumentTextEdit => ({ uri, chars: [], index: expr.range.start.pos - 1, deleteCount: expr.range.end.pos - expr.range.start.pos + 1, }); const addMultiLineIndentation = (buffer: string, indent: string) => { return buffer .split("\n") .map((line) => indent + line) .join("\n"); }; const appendElement = ( uri: string, documents: DocumentManager, engine: EngineDelegate, expr: Element, exprUri: string, edit: AppendChild ): DocumentTextEdit[] => { const [_componentUri, component] = getInstanceComponentInfo(expr, exprUri, engine.getLoadedGraph()) || []; const edits: DocumentTextEdit[] = []; // if appending to instance of component, need to make sure that // {children} exists, otherwise it's a no-op if (component) { const inference = infer(component); if (!inference.properties.children) { return null; } } const source = documents.open(exprUri).openSource().getText(); const tagBuffer = source.substring( expr.openTagRange.start.pos, expr.openTagRange.end.pos ); const [child, additionalChildEdits] = getChildInsertionContent( edit.child, documents, exprUri, engine, false ); const parentIndentation = getExprIndentation(exprUri, engine, expr); // self closing if (tagBuffer.trim().lastIndexOf("/>") !== -1) { const tagSrc = source.substring(expr.range.start.pos, expr.range.end.pos); edits.push({ uri: exprUri, chars: [ ">\n", addMultiLineIndentation(child, parentIndentation + INDENT), `\n${parentIndentation}`, ] .join("") .split(""), // remove WS at end too so that
    isn't converted to
    index: expr.range.start.pos + tagSrc.replace(/\s*\/>$/, "").length, deleteCount: tagSrc.match(/\s*\/>$/)[0].length, }); } else { const indentation = !expr.children.length ? parentIndentation + INDENT : getExprIndentation( exprUri, engine, expr.children[expr.children.length - 1] ); let endTagIndentation = ""; let buffer = child; // if inline, then maintain that if ( expr.range.end.line !== expr.range.start.line || !expr.children.length ) { buffer = "\n" + indentation + child + "\n"; if (!expr.children.length) { buffer += parentIndentation; } } edits.push({ uri: exprUri, chars: buffer.split(""), index: expr.range.end.pos - ``.length, }); } const insertAttrExpr = getAttribute(ELEMENT_INSERT_ATTR, expr); if (insertAttrExpr) { edits.push(deleteAttr(exprUri, insertAttrExpr)); } return [...edits, ...additionalChildEdits]; }; const setTextNodeValue = ( uri: string, engine: EngineDelegate, edit: SetTextNodeValue ) => { const info = getSourceNodeFromPath(uri, engine, edit.nodePath); return { uri: info.textSource.uri, chars: edit.value.split(""), index: info.textSource.range.start.pos, deleteCount: info.textSource.range.end.pos - info.textSource.range.start.pos, }; }; const addFrame = ( uri: string, documents: DocumentManager, engine: EngineDelegate, { child, box }: AddFrame ) => { const info = getSourceNodeFromPath(uri, engine, ""); const [exprUri, expr] = engine.getExpressionById(info.sourceId) as [ string, Fragment | Element | Reference ]; const [childValue, additionalEdits] = getChildInsertionContent( child, documents, exprUri, engine, false ); return [ { uri: exprUri, chars: ( `\n\n\n` + childValue ).split(""), index: expr.range.end.pos, }, ...additionalEdits, ]; }; const getChildInsertionContent = ( insertion: ChildInsertion, documents: DocumentManager, documentUri: string, engine: EngineDelegate, isAttributeValue: boolean ): [string, DocumentTextEdit[]] => { const child = (engine.parseContent(insertion.value, "virt.pc") as Fragment) .children[0]; const isText = child.nodeKind === NodeKind.Text; let buffer = insertion.value; const edits = []; if (insertion.namespaces) { for (const namespace in insertion.namespaces) { // double check to make sure that the namespace is actually used if (!buffer.includes(`<${namespace}.`)) { continue; } const importUri = insertion.namespaces[namespace]; if (importUri === documentUri) { buffer = buffer.replace(new RegExp(`${namespace}.`, "g"), ``); } else { const { ns, edit } = autoAddImport( documentUri, documents, engine, importUri ); buffer = buffer.replace(new RegExp(`${namespace}.`, "g"), `${ns}.`); edits.push(edit); } } } if (isAttributeValue) { if (isText) { buffer = `"${buffer}"`; } else { buffer = `{${buffer}}`; } } return [buffer, edits]; }; const maybeBreak = (buffer: string, ast: Node, isFirstChild: boolean) => { if (isNode(ast) && ast.nodeKind === NodeKind.Element) { return `\n${INDENT}${buffer}\n`; } return buffer; }; const autoAddImport = ( exprUri: string, documents: DocumentManager, engine: EngineDelegate, importUri: string ): { ns: string; edit?: DocumentTextEdit } | undefined => { let ns: string; const ast = engine.getLoadedAst(exprUri) as DependencyNodeContent; const data = engine.getLoadedData(exprUri) as LoadedPCData; const imports = getImports(ast); const importExpr = imports.find( (imp) => data.dependencies[getAttributeStringValue("as", imp)] === importUri || data.dependencies[getAttributeStringValue("src", imp)] === importUri ); if (importExpr) { ns = getAttributeStringValue("as", importExpr); // No namespace? add the attribute if (!ns) { ns = getUniqueImportId(importUri, imports); return { ns, edit: addAttribute(exprUri, engine, { kind: VirtualObjectEditKind.AddAttribute, target: { kind: EditTargetKind.Expression, sourceId: importExpr.id, }, name: "as", value: `"${ns}"`, }), }; } // no import? add it } else { ns = getUniqueImportId(importUri, imports); return { ns, edit: { uri: exprUri, chars: `\n`.split(""), index: 0, }, }; } return { ns }; }; const getUniqueImportId = (uri: string, imports: Element[]) => { let i = 2; const base = camelCase(path.basename(uri).replace(".pc", "")); let ns = base; while (imports.some((imp) => getAttributeStringValue("as", imp) === ns)) { ns = base + i; i++; } return ns; }; ================================================ FILE: packages/paperclip-editor-engine/src/host/documents/pc-source.ts ================================================ import * as Automerge from "automerge"; import * as prettier from "prettier"; import diff from "diff"; import { CRDTTextDocument, SourceDocumentData } from "../../core/crdt-document"; export class PCSourceDocument extends CRDTTextDocument { protected constructor(doc: SourceDocumentData) { super(doc); // this.onChange(this._onChange); } static fromText(text: string) { return new PCSourceDocument( Automerge.from({ text: new Automerge.Text(text) }) ); } // private _onChange = () => { // try { // const now = Date.now(); // const code = this.getText(); // const formattedCode = prettier.format(code, { parser: "paperclip"}); // console.log(diff); // const patch = diff.createPatch("a", code, formattedCode); // console.log(patch); // } catch(e) { // console.error(e); // } // } } ================================================ FILE: packages/paperclip-editor-engine/src/host/documents/pc.ts ================================================ import { EngineDelegate, LoadedPCData } from "@paperclip-ui/core"; import * as Automerge from "automerge"; import { CRDTTextDocument } from "../../core/crdt-document"; import { DocumentKind } from "../../core/documents"; import { BaseDocument } from "./base"; import { EventEmitter } from "events"; import { Logger } from "@paperclip-ui/common"; export class PCDocument extends BaseDocument { readonly kind = DocumentKind.Paperclip; private _source: CRDTTextDocument; /** */ constructor( uri: string, events: EventEmitter, private _engine: EngineDelegate, private _logger: Logger ) { super(uri, events); } /** */ async load2() { try { const ret = this._engine.getLoadedData(this.uri) || this._engine.open(this.uri); return ret as LoadedPCData; } catch (e) { this._logger.error(`Unable to open ${this.uri}`); return null; } } /** */ openSource() { if (this._source) { return this._source; } this._logger.verbose("PCDocument::openSource()", this.uri); this._events.on("incommingCRDTChanges", this._onSourceDocumentCRDTChanges); this._source = CRDTTextDocument.fromText( this._engine.getVirtualContent(this.uri) ); this._source.onChange(this._onSourceChange); return this._source; } /** */ private _onSourceDocumentCRDTChanges = ({ uri, changes }) => { if (uri !== this.uri) { return; } this._logger.verbose("PCDocument::_onSourceDocumentCRDTChanges()"); this._source.applyChanges(changes); }; /** */ private _onSourceChange = (changes: Automerge.BinaryChange[]) => { const text = this._source.getText(); this._engine.updateVirtualFileContent(this.uri, text); this.load(); if (changes.length) { this._events.emit("outgoingCRDTChanges", { uri: this.uri, changes }); } }; } ================================================ FILE: packages/paperclip-editor-engine/src/host/host.ts ================================================ import { EngineDelegate } from "@paperclip-ui/core"; import { Logger, RPCClientAdapter, RPCServer } from "@paperclip-ui/common"; import { EventEmitter } from "events"; import { ClientConnection } from "./connection"; import { DocumentManager } from "./documents/manager"; export class EditorHost { /** */ private _events: EventEmitter; private _documents: DocumentManager; /** */ private constructor( private _engine: EngineDelegate, private _logger: Logger ) { this._events = new EventEmitter(); this._documents = new DocumentManager( this._events, this._engine, this._logger ); } getDocumentManager() { return this._documents; } /** */ static async start( engine: EngineDelegate, server: RPCServer, logger: Logger ) { const host = new EditorHost(engine, logger); await host._listen(server); return host; } /** */ private _listen(server: RPCServer) { server.onConnection((connection) => { this._addConnection(connection); }); } /** */ private _addConnection(connection: RPCClientAdapter) { new ClientConnection( this._documents, connection, this._events, this._engine, this._logger ); } } ================================================ FILE: packages/paperclip-editor-engine/src/index.ts ================================================ ================================================ FILE: packages/paperclip-editor-engine/src/test/basic-test.ts ================================================ import { expect } from "chai"; import { createMockHost, timeout } from "./utils"; import { PCDocument } from "../client/documents"; import { stringifyVirtualNode } from "@paperclip-ui/core"; describe(__filename + "#", () => { it(`Can open a simple document`, async () => { const { server } = await createMockHost({ "/hello.pc": "div", }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); expect((await doc.getSource()).getText()).to.eql("div"); }); it(`Text changes are synchronized between documents`, async () => { const { server } = await createMockHost({ "/hello.pc": "div", }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); const docSource = await doc.getSource(); expect(docSource.getText()).to.eql("div"); const client2 = server.createHostClient(); const doc2 = await client2.getDocuments().open("/hello.pc"); const doc2Source = await doc2.getSource(); docSource.setText("blahh".split("")); expect(await doc2Source.getText()).to.eql("blahhdiv"); }); it(`The engine updates when the source doc changes`, async () => { const { server } = await createMockHost({ "/hello.pc": "Hello World", }); const client = server.createHostClient(); const doc = (await client.getDocuments().open("/hello.pc")) as PCDocument; expect(stringifyVirtualNode(doc.getContent().preview)).to.eql( "Hello World" ); const source = await doc.getSource(); source.setText("abc".split("")); expect(stringifyVirtualNode(doc.getContent().preview)).to.eql( "abcHello World" ); }); it(`Changes from two docs converge into one`, async () => { const { server } = await createMockHost({ "/hello.pc": "Hello World", }); const client = server.createHostClient(true); const doc1 = (await client.getDocuments().open("/hello.pc")) as PCDocument; const src1 = await doc1.getSource(); src1.setText("111".split("")); const client2 = server.createHostClient(true); const doc2 = (await client2.getDocuments().open("/hello.pc")) as PCDocument; const src2 = await doc2.getSource(); src2.setText("222".split("")); await timeout(200); expect(stringifyVirtualNode(doc1.getContent().preview)).to.eql( "222111Hello World" ); }); }); ================================================ FILE: packages/paperclip-editor-engine/src/test/preview-test.ts ================================================ import { PCDocument } from "../client/documents"; import { createMockHost } from "./utils"; import { expect } from "chai"; import { combineFrameHTML2, mockDOMFactory, } from "@paperclip-ui/web-renderer/lib/test/utils"; import { renderFrames, patchFrames } from "@paperclip-ui/web-renderer"; import { stringifyVirtualNode } from "@paperclip-ui/core"; describe(__filename + "#", () => { it(`Can render basic HTML`, async () => { const { server } = await createMockHost({ "/entry.pc": "Hello", }); const client = server.createHostClient(); const doc = (await client.getDocuments().open("/entry.pc")) as PCDocument; expect( combineFrameHTML2( renderFrames(doc.getContent(), { domFactory: mockDOMFactory }) ) ).to.eql(`
    Hello
    `); }); it(`Re-renders based on document changes`, async () => { const { server } = await createMockHost({ "/entry.pc": "Hello", }); const client = server.createHostClient(); const doc = (await client.getDocuments().open("/entry.pc")) as PCDocument; const source = await doc.getSource(); source.setText("blah".split("")); expect( combineFrameHTML2( renderFrames(doc.getContent(), { domFactory: mockDOMFactory }) ) ).to.eql(`
    blahHello
    `); }); it(`Updates imported CSS when the imported CSS changes`, async () => { const { server } = await createMockHost({ "/entry.pc": `
    `, "/imp.pc": ``, }); const client = server.createHostClient(); const doc = (await client.getDocuments().open("/entry.pc")) as PCDocument; const doc2 = (await client.getDocuments().open("/imp.pc")) as PCDocument; const source = await doc2.getSource(); source.setText( ``.split(""), 0, source.getText().length ); expect( combineFrameHTML2( renderFrames(doc.getContent(), { domFactory: mockDOMFactory }) ) ).to.eql( `
    ` ); }); it(`Re-orders style sheets if order has changed`, async () => { const { server } = await createMockHost({ "/entry.pc": `
    `, "/imp.pc": ``, "/imp2.pc": ``, }); const client = server.createHostClient(); const doc = (await client.getDocuments().open("/entry.pc")) as PCDocument; expect( combineFrameHTML2( renderFrames(doc.getContent(), { domFactory: mockDOMFactory }) ) ).to.eql( `
    ` ); const source = await doc.getSource(); source.setText( `
    `.split(""), 0, source.getText().length ); expect( combineFrameHTML2( renderFrames(doc.getContent(), { domFactory: mockDOMFactory }) ) ).to.eql( `
    ` ); }); it(`Can remove an import`, async () => { const { server } = await createMockHost({ "/entry.pc": `
    `, "/imp.pc": ``, "/imp2.pc": ``, }); const client = server.createHostClient(); const doc = (await client.getDocuments().open("/entry.pc")) as PCDocument; expect( combineFrameHTML2( renderFrames(doc.getContent(), { domFactory: mockDOMFactory }) ) ).to.eql( `
    ` ); const source = await doc.getSource(); source.setText( `
    `.split(""), 0, source.getText().length ); expect( combineFrameHTML2( renderFrames(doc.getContent(), { domFactory: mockDOMFactory }) ) ).to.eql( `
    ` ); }); it(`Can add an import`, async () => { const { server } = await createMockHost({ "/entry.pc": `
    `, "/imp.pc": ``, "/imp2.pc": `afdsfdsfds`, }); const client = server.createHostClient(); const doc = (await client.getDocuments().open("/entry.pc")) as PCDocument; expect( combineFrameHTML2( renderFrames(doc.getContent(), { domFactory: mockDOMFactory }) ) ).to.eql( `
    ` ); const source = await doc.getSource(); source.setText( `
    `.split(""), 0, source.getText().length ); expect( combineFrameHTML2( renderFrames(doc.getContent(), { domFactory: mockDOMFactory }) ) ).to.eql( `
    ` ); }); it(`Updates content when local sheet changes`, async () => { const { server } = await createMockHost({ "/entry.pc": `
    `, }); const client = server.createHostClient(); const doc = (await client.getDocuments().open("/entry.pc")) as PCDocument; const source = await doc.getSource(); source.setText( `
    `.split(""), 0, source.getText().length ); expect( combineFrameHTML2( renderFrames(doc.getContent(), { domFactory: mockDOMFactory }) ) ).to.eql( `
    ` ); }); it(`Imported CSS can be updated`, async () => { const { server, engine } = await createMockHost({ "/entry.pc": `
    `, "/imp.css": `div { color: blue }`, }); const client = server.createHostClient(); const doc = (await client.getDocuments().open("/entry.pc")) as PCDocument; // const doc2 = (await client.getDocuments().open("/imp.css")) as PCDocument; // cheat engine.updateVirtualFileContent("/imp.css", `div { color: orange }`); // const source = await doc2.getSource(); // source.setText( // `div { color: orange }`.split(""), // 0, // source.getText().length // ); expect( combineFrameHTML2( renderFrames(doc.getContent(), { domFactory: mockDOMFactory }) ) ).to.eql( `
    ` ); }); }); ================================================ FILE: packages/paperclip-editor-engine/src/test/utils.ts ================================================ import { createMockRPCServer, Logger, LogLevel } from "@paperclip-ui/common"; import { EventEmitter } from "events"; import { EditorHost } from "../host/host"; import { createMockEngine } from "@paperclip-ui/core/lib/test/utils"; import { EditorClient } from "../client/client"; import { EngineMode } from "@paperclip-ui/core"; export const createMockServer = () => { const hostEm = new EventEmitter(); const rpcServer = createMockRPCServer(); return { onConnection(listener) { rpcServer.onConnection(listener); }, createHostClient(delay?: boolean) { return new EditorClient(rpcServer.createConnection(delay)); }, }; }; export const createMockHost = async (graph: Record) => { const server = createMockServer(); const engine = createMockEngine(graph, null, null, EngineMode.SingleFrame); const host = await EditorHost.start( engine, server, new Logger(LogLevel.None) ); return { host, server, engine }; }; export const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); ================================================ FILE: packages/paperclip-editor-engine/src/test/virtual-edit-test.ts ================================================ import { computeVirtScriptObject, ELEMENT_INSERT_ATTR, Fragment, Node, stringifyVirtualNode, VirtualElement, } from "@paperclip-ui/core"; import { expect } from "chai"; import { AppendChild, EditTargetKind, VirtualObjectEditKind } from "../core"; import { createMockHost } from "./utils"; // TODO - test latency describe(__filename + "#", () => { describe("html", () => { it(`Can update the text of a virtual node`, async () => { const { server } = await createMockHost({ "/hello.pc": "
    blah
    ", }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); doc.editVirtualObjects([ { kind: VirtualObjectEditKind.InsertNodeBefore, beforeNodePath: "0.0", node: { value: "" }, }, ]); expect(stringifyVirtualNode(doc.getContent().preview)).to.eql( `
    blah
    ` ); }); it(`Can update the text of a node`, async () => { const { server } = await createMockHost({ "/hello.pc": "
    blah
    ", }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); doc.editVirtualObjects([ { kind: VirtualObjectEditKind.SetTextNodeValue, nodePath: "0.0", value: "Hello world", }, ]); expect(stringifyVirtualNode(doc.getContent().preview)).to.eql( `
    Hello world
    ` ); }); it(`Can insert new node annotations`, async () => { const { server } = await createMockHost({ "/hello.pc": "
    blah
    ", }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); const annotations = { tags: ["a", "b"], desc: "Some description", frame: { width: 100, height: 100 }, }; doc.editVirtualObjects([ { kind: VirtualObjectEditKind.SetAnnotations, nodePath: "0", value: annotations, }, ]); const node = doc.getNodeFromPath("0") as VirtualElement; expect(annotations).to.eql(computeVirtScriptObject(node.annotations)); expect(stringifyVirtualNode(doc.getContent().preview)).to.eql( `
    blah
    ` ); }); it(`can insert annotations with quites`, async () => { const { server } = await createMockHost({ "/hello.pc": "
    blah
    ", }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); const annotations = { tags: ["a", "b"], desc: "Some description", frame: { x: 834, height: 817, y: "-21", width: "100", title: "Something", }, }; doc.editVirtualObjects([ { kind: VirtualObjectEditKind.SetAnnotations, nodePath: "0", value: annotations, }, ]); const node = doc.getNodeFromPath("0") as VirtualElement; expect(annotations).to.eql(computeVirtScriptObject(node.annotations)); expect(stringifyVirtualNode(doc.getContent().preview)).to.eql( `
    blah
    ` ); }); it(`Can update existing annotations`, async () => { const { server } = await createMockHost({ "/hello.pc": `
    blah
    `, }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); const annotations = { tags: ["a", "b"], desc: "Some description", frame: { width: 100, height: 100 }, }; doc.editVirtualObjects([ { kind: VirtualObjectEditKind.SetAnnotations, nodePath: "0", value: annotations, }, ]); const node = doc.getNodeFromPath("0") as VirtualElement; const source = await doc.getSource(); expect(source.getText().replace(/[\s\n]+/g, " ")).to.eq( `
    blah
    ` ); expect(annotations).to.eql(computeVirtScriptObject(node.annotations)); expect(stringifyVirtualNode(doc.getContent().preview)).to.eql( `
    blah
    ` ); }); it(`Can add a new element attribute`, async () => { const { server } = await createMockHost({ "/hello.pc": "
    blah
    ", }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); doc.editVirtualObjects([ { kind: VirtualObjectEditKind.AddAttribute, target: { kind: EditTargetKind.VirtualNode, nodePath: "0" }, name: "a", value: '"b"', }, ]); expect(stringifyVirtualNode(doc.getContent().preview)).to.eql( `
    blah
    ` ); }); xit(`Can update an element attribute value`, async () => { const { server } = await createMockHost({ "/hello.pc": "
    blah
    ", }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); doc.editVirtualObjects([ { kind: VirtualObjectEditKind.UpdateAttribute, nodePath: "0", name: "a", value: '"b"', }, ]); expect(stringifyVirtualNode(doc.getContent().preview)).to.eql( `
    blah
    ` ); }); it(`Can append a child to a self-closing element`, async () => { const { server } = await createMockHost({ "/hello.pc": "
    ", }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); doc.editVirtualObjects([ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0", child: { value: "" }, }, ]); expect(stringifyVirtualNode(doc.getContent().preview)).to.eql( `
    ` ); }); it(`Can append a child to an element with a closing tag`, async () => { const { server } = await createMockHost({ "/hello.pc": "
    ", }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); doc.editVirtualObjects([ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0", child: { value: "" }, }, ]); expect(stringifyVirtualNode(doc.getContent().preview)).to.eql( `
    ` ); }); it(`Can append a child to an element with children`, async () => { const { server } = await createMockHost({ "/hello.pc": "
    abba
    ", }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); doc.editVirtualObjects([ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0", child: { value: "" }, }, ]); expect(stringifyVirtualNode(doc.getContent().preview)).to.eql( `
    abba
    ` ); }); it(`If text is inserted into a slot placeholder, that slot is assigned as a regular key/value attribute`, async () => { const { server } = await createMockHost({ "/hello.pc": `
    {child}
    `, }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); const source = await doc.getSource(); expect(stringifyVirtualNode(doc.getContent().preview, "[slot]")).to.eql( `
    [slot]
    ` ); doc.editVirtualObjects([ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0.0", child: { value: "blarg" }, }, ]); expect(source.getText()).to.eql( `
    {child}
    ` ); expect(stringifyVirtualNode(doc.getContent().preview)).to.eq( `
    blarg
    ` ); }); it(`If an element is inserted into a slot placeholder, that slot is assigned as a regular key/script attribute`, async () => { const { server } = await createMockHost({ "/hello.pc": `
    {child}
    `, }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); const source = await doc.getSource(); expect(stringifyVirtualNode(doc.getContent().preview, "[slot]")).to.eql( `
    [slot]
    ` ); doc.editVirtualObjects([ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0.0", child: { value: "" }, }, ]); expect(source.getText()).to.eql( `
    {child}
    } />` ); expect(stringifyVirtualNode(doc.getContent().preview)).to.eq( `
    ` ); }); it(`Can remove a node`, async () => { const { server } = await createMockHost({ "/hello.pc": `ab`, }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); const source = await doc.getSource(); expect(stringifyVirtualNode(doc.getContent().preview, "[slot]")).to.eql( `ab` ); doc.editVirtualObjects([ { kind: VirtualObjectEditKind.DeleteNode, nodePath: "1", }, ]); expect(source.getText()).to.eql(`ab`); }); it(`When removing a node, the annotations are also removed if present`, async () => { const { server } = await createMockHost({ "/hello.pc": `node`, }); const client = server.createHostClient(); const doc = await client.getDocuments().open("/hello.pc"); const source = await doc.getSource(); expect(stringifyVirtualNode(doc.getContent().preview, "[slot]")).to.eql( `node` ); doc.editVirtualObjects([ { kind: VirtualObjectEditKind.DeleteNode, nodePath: "0", }, ]); expect(source.getText()).to.eql(``); }); [ [ `When annotations are added to an element without them, a new line is also created`, { "/hello.pc": "
    ", }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.SetAnnotations, value: { a: "b" }, nodePath: "0", }, ], `\n
    `, ], }, ], [ `Can insert a text node as a frame`, { "/hello.pc": "
    ", }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AppendChild, child: { value: "text" }, }, ], `
    text`, ], }, ], [ `Can insert a new frame`, { "/hello.pc": "
    ", }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AddFrame, child: { value: "text" }, box: { x: 100, y: 100.1, width: 100, height: 100 }, }, ], `
    \n\n\ntext`, ], }, ], [ `Can insert a new instance frame`, { "/hello.pc": `
    `, "/hello2.pc": `
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AddFrame, child: { namespaces: { abba: "/hello2.pc" }, value: ``, }, box: { x: 100, y: 100.1, width: 100, height: 100 }, }, ], `
    \n\n\n`, ], }, ], [ `Adds a ns to an import if including an instance from another doc`, { "/hello.pc": `
    `, "/hello2.pc": `
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AddFrame, child: { namespaces: { abba: "/hello2.pc" }, value: ``, }, box: { x: 100, y: 100.1, width: 100, height: 100 }, }, ], `
    \n\n\n`, ], }, ], [ `If inserting an instance of element that's not imported, the import is added`, { "/hello.pc": `
    `, "/hello2.pc": `
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AddFrame, child: { namespaces: { abba: "/hello2.pc" }, value: ``, }, box: { x: 100, y: 100.1, width: 100, height: 100 }, }, ], `\n
    \n\n\n`, ], }, ], [ `When inserting an instance into an element, the instance is auto-imported`, { "/hello.pc": `
    `, "/hello2.pc": `
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0", child: { namespaces: { abba: "/hello2.pc" }, value: ``, }, }, ], `\n
    \n \n
    `, ], }, ], [ `Auto-imports a module if an import shares the same NS but not source`, { "/hello.pc": `
    `, "/hello2.pc": `
    `, "/hello3.pc": `
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AddFrame, child: { namespaces: { abba: "/hello3.pc" }, value: ``, }, box: { x: 100, y: 100.1, width: 100, height: 100 }, }, ], `\n
    \n\n\n`, ], }, ], [ `Can add a child to a self-closing element at the end of the document`, { "/hello.pc": `
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AppendChild, nodePath: "1", child: { value: "Hello", }, }, ], `
    \n Hello\n
    `, ], }, ], [ `Can insert a instance of a component within the same document`, { "/hello.pc": `
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AppendChild, child: { namespaces: { abba: "/hello.pc" }, value: ``, }, }, ], `
    `, ], }, ], [ `When inserting an instance into a slot of an instance that's also imported, the import is added to the slot instance doc`, { "/hello.pc": ``, "/hello2.pc": `
    {child}
    `, "/hello3.pc": `
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0.0", child: { namespaces: { abba: "/hello3.pc" }, value: ``, }, }, ], `\n} />`, ], }, ], [ `When inserting a child into a children slot, the child is added as a regular child of an element`, { "/hello.pc": `
    {children}
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0.0", child: { value: "Something", }, }, ], `
    {children}
    \n Something\n`, ], }, ], [ `Appending child to instance without children slot results in no-op`, { "/hello.pc": `
    {child}
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0", child: { value: "Something", }, }, ], `
    {child}
    `, ], }, ], [ `Can delete a nested element`, { "/hello.pc": `
    abc
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.DeleteNode, nodePath: "0.0", }, ], `
    `, ], }, ], [ `When deleting a slotted string attribute, the attribute is removed`, { "/hello.pc": `
    {child}
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.DeleteNode, nodePath: "0.0", }, ], `
    {child}
    `, ], }, ], [ `When deleting a slotted node attribute, the attribute is removed`, { "/hello.pc": `
    {child}
    } />`, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.DeleteNode, nodePath: "0.0", }, ], `
    {child}
    `, ], }, ], [ `When deleting a slotted script attribute, the attribute is removed`, { "/hello.pc": `
    {child}
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.DeleteNode, nodePath: "0.0", }, ], `
    {child}
    `, ], }, ], [ `If deleting a child of a slotted element, that child is deleted`, { "/hello.pc": `
    {child}
    to delete
    } />`, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.DeleteNode, nodePath: "0.0.0", }, ], `
    {child}
    } />`, ], }, ], [ `When appending to element with ${ELEMENT_INSERT_ATTR}, the ${ELEMENT_INSERT_ATTR} attr is removed`, { "/hello.pc": `
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0", child: { value: "" }, }, ], `
    \n \n
    `, ], }, ], [ `If appending to an element without children, the indentation is based on parent spacing`, { "/hello.pc": `
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0", child: { value: "" }, }, ], `
    \n \n
    `, ], }, ], [ `If appending to an element with a child, the indentation is maintained based on the previous child`, { "/hello.pc": `
    \n child
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0", child: { value: "" }, }, ], `
    \n child\n \n
    `, ], }, ], [ `When adding a child to a nested self-closing element, the parent end tag matches the start`, { "/hello.pc": `\n \n`, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0.0", child: { value: "" }, }, ], `\n \n \n \n`, ], }, ], [ `Can append a child to a self-closing element with an insertion attr`, { "/hello.pc": `
    `, }, { "/hello.pc": [ [ { kind: VirtualObjectEditKind.AppendChild, nodePath: "0", child: { value: "" }, }, ], `
    \n \n
    `, ], }, ], [ `Can change a style declaration on a specific declaration ID`, { "/hello.pc": `
    `, }, { "/hello.pc": [ (ast: Fragment) => { // console.log(JSON.stringify(ast, null, 2)); return [ { kind: VirtualObjectEditKind.SetStyleDeclaration, target: { kind: EditTargetKind.Expression, sourceId: "5cd17222-1-1", }, name: "width", value: "999px", }, ]; }, `
    `, ], }, ], [ `When setting a style declaration on an element that doesn't have a style block, a new style block is added`, { "/hello.pc": `
    `, }, { "/hello.pc": [ (ast: Fragment) => { return [ { kind: VirtualObjectEditKind.SetStyleDeclaration, target: { kind: EditTargetKind.VirtualNode, nodePath: "0" }, name: "width", value: "999px", }, ]; }, `
    \n \n
    `, ], }, ], [ `When setting a style declaration on an element that doesn't have a style block and has a children, the style is prepended to the beginning`, { "/hello.pc": `
    \n \n
    `, }, { "/hello.pc": [ (ast: Fragment) => { return [ { kind: VirtualObjectEditKind.SetStyleDeclaration, target: { kind: EditTargetKind.VirtualNode, nodePath: "0" }, name: "width", value: "999px", }, ]; }, `
    \n \n \n
    `, ], }, ], [ `When setting a style declaration on an element with a style and the declaration doesn't exist, the declaration is added`, { "/hello.pc": `
    \n \n
    `, }, { "/hello.pc": [ (ast: Fragment) => { return [ { kind: VirtualObjectEditKind.SetStyleDeclaration, target: { kind: EditTargetKind.VirtualNode, nodePath: "0" }, name: "height", value: "100px", }, ]; }, `
    \n \n
    `, ], }, ], [ `When setting a style declaration on an element with a style that includes an existing declaration, the declaration is added at the end`, { "/hello.pc": `
    \n \n
    `, }, { "/hello.pc": [ (ast: Fragment) => { return [ { kind: VirtualObjectEditKind.SetStyleDeclaration, target: { kind: EditTargetKind.VirtualNode, nodePath: "0" }, name: "height", value: "100px", }, ]; }, `
    \n \n
    `, ], }, ], [ `When setting a style that already exists on an element, that style is replaced`, { "/hello.pc": `
    \n \n
    `, }, { "/hello.pc": [ (ast: Fragment) => { return [ { kind: VirtualObjectEditKind.SetStyleDeclaration, target: { kind: EditTargetKind.VirtualNode, nodePath: "0" }, name: "background", value: "blue", }, ]; }, `
    \n \n
    `, ], }, ], [ `Can rename a style on a style element`, { "/hello.pc": `
    \n \n
    `, }, { "/hello.pc": [ (ast: Fragment) => { return [ { kind: VirtualObjectEditKind.SetStyleDeclaration, target: { kind: EditTargetKind.VirtualNode, nodePath: "0" }, name: "color", oldName: "background", value: "blue", }, ]; }, `
    \n \n
    `, ], }, ], [ `Clearing the name of a declaration removes it`, { "/hello.pc": `
    \n \n
    `, }, { "/hello.pc": [ (ast: Fragment) => { return [ { kind: VirtualObjectEditKind.SetStyleDeclaration, target: { kind: EditTargetKind.VirtualNode, nodePath: "0" }, name: "", oldName: "background", value: "blue", }, ]; }, `
    \n \n
    `, ], }, ], [ `Clearing the value of a declaration removes it`, { "/hello.pc": `
    \n \n
    `, }, { "/hello.pc": [ (ast: Fragment) => { return [ { kind: VirtualObjectEditKind.SetStyleDeclaration, target: { kind: EditTargetKind.VirtualNode, nodePath: "0" }, name: "background", value: "", }, ]; }, `
    \n \n
    `, ], }, ], ].forEach(([name, graph, change]: any) => { it(name, async () => { const { server, engine } = await createMockHost(graph); const client = server.createHostClient(); for (const fileName in change) { const [edits, expected] = change[fileName]; const doc = await client.getDocuments().open(fileName); const source = await doc.getSource(); doc.editVirtualObjects( typeof edits === "function" ? edits(engine.getLoadedAst(fileName)) : edits ); expect(source.getText()).to.eql(expected); } }); }); }); }); ================================================ FILE: packages/paperclip-editor-engine/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./lib", "target": "es5", "moduleResolution": "node", "declaration": true, "module": "commonjs", "allowSyntheticDefaultImports": true, "strict": false /* Enable all strict type-checking options. */ }, "exclude": [ "*.config.ts", "./jest.config.ts", "**/*.d.ts", "lib/**/*.d.ts", "esm", "native", "lib/**/*", "node_modules", "*-test", "index.d.ts", "browser.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-interim/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/paperclip-interim/README.md ================================================ Interimittent compiler step. ```javascript import { InterimCompiler } from "@paperclip-ui/interim"; import { translateReact } from "@paperclip-ui/compiler-react"; const interm = new InterimCompiler(engine); const output = translateReact(interm); ``` TODOs: - [ ] source maps ================================================ FILE: packages/paperclip-interim/package.json ================================================ { "name": "@paperclip-ui/interim", "version": "18.5.6", "description": "", "main": "lib/index.js", "publishConfig": { "access": "public" }, "scripts": { "build": "tsc", "build:watch": "tsc --watch --preserveWatchOutput", "test": "mocha lib/test" }, "author": "", "license": "ISC", "dependencies": { "@paperclip-ui/core": "^18.5.6", "@paperclip-ui/utils": "^18.5.6", "crc32": "^0.2.2", "mime": "^3.0.0" }, "devDependencies": { "@types/mocha": "^8.0.0", "mocha": "^7.0.1", "typescript": "^4.2.3" }, "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a" } ================================================ FILE: packages/paperclip-interim/src/index.ts ================================================ export * from "./translate"; export * from "./state"; ================================================ FILE: packages/paperclip-interim/src/state/assets.ts ================================================ export type InterimAsset = { originalPath: string; // absolute file path to the asset filePath: string; outputFilePath: string; // file content if embedded moduleContent?: string; }; ================================================ FILE: packages/paperclip-interim/src/state/css.ts ================================================ export type CSSExports = { classNames: Record; }; export type InterimCSS = { sheetText: string; exports: CSSExports; // TODO sourceMaps?: any; }; ================================================ FILE: packages/paperclip-interim/src/state/html.ts ================================================ import { Inference, ShapeInference } from "@paperclip-ui/core"; import { NodeKind, StringRange } from "@paperclip-ui/utils"; import { InterimScriptExpression, InterimScriptExpressionKind } from "./script"; export enum InterimNodeKind { Element = "Element", Component = "Component", Text = "Text", Slot = "Slot" } export type InterimBaseNode = { kind: TKind; range: StringRange; }; type BaseElement = { id: string; tagName: string; attributes: Record; innerTagName?: string; // imp isInstance: boolean; scopeClassNames: string[]; } & InterimBaseNode; export type InterimComponent = { as: string; // as attribute exported: boolean; children: InterimNode[]; schema: ShapeInference; } & BaseElement; export enum InterimAttributeValuePartKind { Static = "Static", Dynamic = "Dynamic", Shorthand = "Shorthand" } type BaseAttributeValuePart = { kind: TKind; range: StringRange; }; export type DynamicAttributeValuePart = { script: InterimScriptExpression; } & BaseAttributeValuePart; export type ShorthandAttributeValuePart = {} & BaseAttributeValuePart< InterimAttributeValuePartKind.Shorthand >; export type StaticAttributeValuePart = { value: string; } & BaseAttributeValuePart; export type InterimAttributeValuePart = | DynamicAttributeValuePart | StaticAttributeValuePart | ShorthandAttributeValuePart; export type InterimAttributeValue = { range?: StringRange; // className:a="b" variantName?: string; // className:a="{aClass?}" parts?: InterimAttributeValuePart[]; }; export type InterimAttribute = { // value parts - literal or dynamic. Covers className="a {className?}" and such variants: InterimAttributeValue[]; }; export type InterimElement = { children: InterimNode[]; isInstance: boolean; } & BaseElement; export type InterimText = { value: string; } & InterimBaseNode; export type InterimSlotNode = { script: InterimScriptExpression; } & InterimBaseNode; export type InterimNode = | InterimElement | InterimText | InterimComponent | InterimSlotNode; export type InterimExpression = InterimNode | InterimScriptExpression; export const traverseInterimExpression = ( expr: InterimExpression, each: (descendent: InterimExpression) => boolean ) => { if (each(expr) === false) { return false; } if ( expr.kind === InterimNodeKind.Component || expr.kind === InterimNodeKind.Element ) { expr.children.forEach(child => { traverseInterimExpression(child, each); }); } else if (expr.kind === InterimNodeKind.Slot) { traverseInterimExpression(expr.script, each); } else if (expr.kind === InterimScriptExpressionKind.Element) { traverseInterimExpression(expr.element, each); } else if (expr.kind === InterimScriptExpressionKind.Group) { traverseInterimExpression(expr.inner, each); } else if (expr.kind === InterimScriptExpressionKind.Conjunction) { traverseInterimExpression(expr.left, each) && traverseInterimExpression(expr.right, each); } }; ================================================ FILE: packages/paperclip-interim/src/state/index.ts ================================================ export * from "./module"; export * from "./html"; export * from "./css"; export * from "./script"; export * from "./options"; ================================================ FILE: packages/paperclip-interim/src/state/module.ts ================================================ import { InterimAsset } from "./assets"; import { InterimCSS } from "./css"; import { InterimComponent } from "./html"; export type InterimImport = { namespace?: string; publicScopeId: string; filePath: string; relativePath: string; usedTagNames: string[]; injectedStyles: boolean; }; export type InterimModule = { imports: InterimImport[]; // exported components components: InterimComponent[]; // compiled CSS in this doc -- dev just needs to include generated text // wherever and load in the module css: InterimCSS; // assets (svg, jpg, etc) embedded in this doc. assets: InterimAsset[]; }; ================================================ FILE: packages/paperclip-interim/src/state/options.ts ================================================ import { PaperclipConfig } from "@paperclip-ui/core"; import { CompilerOptions } from "@paperclip-ui/utils"; import { InterimModule } from "."; export type CompileOptions = { module: InterimModule; fileUrl: string; includes: string[]; config: PaperclipConfig; targetOptions: CompilerOptions; cwd: string; }; export type TargetCompiler = ( options: CompileOptions ) => Record; ================================================ FILE: packages/paperclip-interim/src/state/script.ts ================================================ import { StringRange } from "@paperclip-ui/utils"; import { InterimElement } from "./html"; export enum InterimScriptExpressionKind { Reference = "Reference", Conjunction = "Conjunction", Number = "Number", Boolean = "Boolean", Group = "Group", Not = "Not", String = "String", Element = "ScriptElement" } type BaseInterimScriptExpression = { kind: TKind; range: StringRange; }; export enum InterimConjunctionOperator { Or = "Or", And = "And" } export type InterimConjunction = { operator: InterimConjunctionOperator; left: InterimScriptExpression; right: InterimScriptExpression; } & BaseInterimScriptExpression; export type InterimReference = { name?: string; optional?: boolean; } & BaseInterimScriptExpression; export type InterimNumber = { value: string; } & BaseInterimScriptExpression; export type InterimString = { value: string; } & BaseInterimScriptExpression; export type InterimGroup = { inner: InterimScriptExpression; } & BaseInterimScriptExpression; export type InterimBoolean = { value: boolean; } & BaseInterimScriptExpression; export type InterimNot = { expression: InterimScriptExpression; } & BaseInterimScriptExpression; export type InterimScriptEelement = { kind: InterimScriptExpressionKind; element: InterimElement; } & BaseInterimScriptExpression; export type InterimScriptExpression = | InterimNot | InterimString | InterimGroup | InterimNumber | InterimBoolean | InterimReference | InterimScriptEelement | InterimConjunction; ================================================ FILE: packages/paperclip-interim/src/test/basic-test.ts ================================================ import { expect } from "chai"; import { createMockEngine } from "@paperclip-ui/core/lib/test/utils"; import { InterimCompiler } from ".."; import { stringifyInterimModule } from "./utils"; describe(__filename + "#", () => { [ [ `can convert a simple AST to a module`, { "/src/entry.pc": `
    `, }, {}, { html: "
    " }, ], [ `Can embed assets`, { "/src/entry.pc": `
    {}
    `, "/a.svg": "embedded-svg", "/b.svg": "not-embedded-svg", "/c.svg": "embedded", "/d.svg": "embed", "/e.svg": "embed2", }, { embedAssetMaxSize: "embedded-svg".length, useAssetHashNames: false, }, { html: `
    `, }, ], [ `Renames assets to assetOutDir`, { "/src/entry.pc": `
    {}
    `, "/src/a.svg": "embedded-svg", "/src/b.svg": "not-embedded-svg", "/src/c.svg": "embedded", "/src/d.svg": "embed", "/src/e.svg": "embed2", }, { assetOutDir: "./lib", srcDir: "/src", useAssetHashNames: false, }, { html: `
    `, }, ], [ `Can embed assets and emit files that exceed max size`, { "/src/entry.pc": `
    {}
    `, "/a.svg": "embedded-svg", "/b.svg": "not-embedded-svg", "/c.svg": "embedded", "/d.svg": "embed", "/e.svg": "embed2", }, { embedAssetMaxSize: "embedded-svg".length, assetOutDir: "./lib", srcDir: "/src", outDir: "/lib", }, { html: `
    `, }, ], [ `Can define an asset prefix`, { "/src/entry.pc": `
    {}
    `, "/a.svg": "embedded-svg", "/b.svg": "not-embedded-svg", "/c.svg": "embedded", "/d.svg": "embed", "/e.svg": "embed2", }, { embedAssetMaxSize: "embedded-svg".length, assetOutDir: "./lib", srcDir: "/src", outDir: "/lib", assetPrefix: "http://localhost:3000/", }, { html: `
    `, }, ], [ `Includes PC imports`, { "/src/entry.pc": ` `, "/a.pc": "
    ", }, { embedAssetMaxSize: "embedded-svg".length, assetOutDir: "./lib", srcDir: "/src", outDir: "/lib", assetPrefix: "http://localhost:3000/", }, { html: ``, }, ], [ `Omits css imports`, { "/src/entry.pc": ` `, "/a.pc": "
    ", "/a.css": ".a { }", }, { embedAssetMaxSize: "embedded-svg".length, assetOutDir: "./lib", srcDir: "/src", outDir: "/lib", assetPrefix: "http://localhost:3000/", }, { html: ``, }, ], [ `Includes CSS imports if importAssetsAsModules is true`, { "/src/entry.pc": ` `, "/a.pc": "
    ", "/a.css": ".a { }", }, { importAssetsAsModules: true, }, { html: ``, }, ], [ `Namespaces can be injected`, { "/src/entry.pc": `
    `, "/a.css": ".a { }", }, { importAssetsAsModules: true, }, { html: `
    `, }, ], ].forEach( ([ title, graph, { embedAssetMaxSize, assetOutDir, srcDir, outDir, assetPrefix, useAssetHashNames, importAssetsAsModules, }, expectedOutput, ]: any) => { it(title, () => { const engine = createMockEngine(graph); const interim = new InterimCompiler(engine, { cwd: "/", config: { srcDir, }, targetOptions: { outDir, importAssetsAsModules, assetPrefix, embedAssetMaxSize, assetOutDir, useAssetHashNames, }, io: { readFile(filePath) { return Buffer.from(graph[filePath]); }, getFileSize(filePath: string) { return graph[filePath].length; }, }, }); const module = interim.parseFile("/src/entry.pc"); expect(stringifyInterimModule(module)).to.eql(expectedOutput.html); }); } ); }); ================================================ FILE: packages/paperclip-interim/src/test/utils.ts ================================================ import { InterimAttribute, InterimAttributeValue, InterimAttributeValuePart, InterimAttributeValuePartKind, InterimComponent, InterimExpression, InterimModule, InterimNode, InterimNodeKind, InterimScriptExpressionKind } from ".."; export const stringifyInterimModule = (module: InterimModule) => { const buffer = [ `${module.imports .map(imp => ``) .join("")}` ]; buffer.push(...module.components.map(stringifyNode)); return buffer.join(" "); }; export const stringifyNode = (node: InterimExpression) => { switch (node.kind) { case InterimNodeKind.Element: case InterimNodeKind.Component: { const buffer = [`<`, node.tagName]; for (const name in node.attributes) { buffer.push(` ${name}=${stringifyAttribute(node.attributes[name])}`); } buffer.push(">"); buffer.push(...node.children.map(stringifyNode)); buffer.push(""); return buffer.join(""); } case InterimNodeKind.Slot: { return stringifyNode(node.script); } case InterimScriptExpressionKind.Element: { return stringifyNode(node.element); } case InterimNodeKind.Text: { return `[TEXT]`; } } }; const stringifyAttribute = (attr: InterimAttribute) => { return attr.variants.map(variant => { const buffer = []; if (variant.variantName) { buffer.push(variant.variantName, "?"); } if (variant.parts) { buffer.push(...variant.parts.map(stringifyVariantPart)); } return buffer.join("+"); }); }; const stringifyVariantPart = (part: InterimAttributeValuePart) => { switch (part.kind) { case InterimAttributeValuePartKind.Dynamic: { return `[SCRIPT]`; } case InterimAttributeValuePartKind.Shorthand: { return `[SELF]`; } case InterimAttributeValuePartKind.Static: { return part.value; } } }; ================================================ FILE: packages/paperclip-interim/src/translate/assets.ts ================================================ import * as path from "path"; import * as URL from "url"; import { VirtSheet, stringifyCSSSheet, VirtRule, VirtRuleKind, VirtStyleRule, VirtFontFace, Expression, Node, traverseExpression, isAttribute, AttributeKind, AttributeValueKind, isNode, NodeKind, } from "@paperclip-ui/utils"; import { InterimCompilerOptions } from "./options"; import { InterimAsset } from "../state/assets"; import { EngineDelegate } from "@paperclip-ui/core"; import * as mime from "mime"; import * as crypto from "crypto"; export const getAssets = ( modulePath: string, node: Node | null, sheet: VirtSheet, engine: EngineDelegate, options: InterimCompilerOptions ): InterimAsset[] => { const outModulePath = options.targetOptions.outDir ? modulePath.replace( path.join(options.cwd, options.config.srcDir), path.join(options.cwd, options.targetOptions.outDir) ) : modulePath; const { html, css } = collectAssetPaths(node, sheet); return [ ...html.map(mapAsset(modulePath, outModulePath, engine, options)), ...css.map( mapAsset( modulePath, options.targetOptions.mainCSSFileName ? path.join( options.cwd, options.targetOptions.assetOutDir || options.targetOptions.outDir, options.targetOptions.mainCSSFileName ) : outModulePath, engine, options ) ), ]; }; const mapAsset = ( modulePath: string, outModulePath: string, engine: EngineDelegate, options: InterimCompilerOptions ) => (originalPath: string) => { let relativeAssetPath = originalPath; if (relativeAssetPath.indexOf("file") === 0) { relativeAssetPath = URL.fileURLToPath(relativeAssetPath); relativeAssetPath = resolvePath(modulePath, relativeAssetPath); } const filePath = engine.resolveFile(modulePath, relativeAssetPath); let moduleContent: string; let outputFilePath = filePath; const fileSize = options.io.getFileSize(filePath); if ( fileSize <= options.targetOptions.embedAssetMaxSize || options.targetOptions.embedAssetMaxSize === -1 ) { moduleContent = `data:${mime.getType(filePath)};base64,` + options.io.readFile(filePath).toString("base64"); } else if (options.targetOptions.assetOutDir) { const srcDir = path.join(options.cwd, options.config.srcDir); const outputDir = path.join( options.cwd, options.targetOptions.assetOutDir ); if (options.targetOptions.useAssetHashNames !== false) { const buffer = options.io.readFile(filePath); const md5Name = crypto.createHash("md5").update(buffer).digest("hex"); outputFilePath = path.join(outputDir, md5Name + path.extname(filePath)); } else { outputFilePath = path.join(outputDir, filePath.replace(srcDir, "")); } if (options.targetOptions.assetPrefix) { moduleContent = options.targetOptions.assetPrefix + path.relative(options.cwd, outputFilePath); } else { moduleContent = resolvePath(outModulePath, outputFilePath); } } return { originalPath, filePath, outputFilePath, moduleContent, }; }; const collectAssetPaths = (root: Node | null, sheet: VirtSheet) => { const html = {}; const css = {}; if (root) { traverseExpression(root, null, (nested) => { if ( isNode(nested) && nested.nodeKind === NodeKind.Element && nested.tagName === "import" ) { return false; } if (isAttribute(nested)) { if ( nested.attrKind === AttributeKind.KeyValueAttribute && nested.name === "src" && nested.value.attrValueKind === AttributeValueKind.String ) { html[nested.value.value] = 1; } } }); } traverseVirtSheet(sheet, (rule) => { if (rule.style) { for (const { value } of rule.style) { if (/url\(/.test(value)) { const parts = value.match(/url\(['"]?(.*?)['"]?\)/); let url = parts && parts[1]; if (url && !url.includes("http")) { css[url] = 1; } } } } }); return { html: Object.keys(html), css: Object.keys(css), }; }; const resolvePath = (from: string, to: string) => { let resolved = path.relative(path.dirname(from), to); if (resolved.charAt(0) !== ".") { resolved = "./" + resolved; } return resolved; }; const traverseVirtSheet = ( sheet: VirtSheet, each: (rule: VirtStyleRule | VirtFontFace) => void ) => { const each2 = (rule: VirtRule) => { if (rule.kind === VirtRuleKind.Media) { for (const rule2 of rule.rules) { each2(rule2); } } else { each(rule); } }; for (const rule of sheet.rules) { each2(rule); } }; ================================================ FILE: packages/paperclip-interim/src/translate/css.ts ================================================ import { CSSExports, PCExports, stringifyCSSSheet, VirtSheet } from "@paperclip-ui/utils"; import { InterimCSS } from "../state"; import { InterimAsset } from "../state/assets"; export const translateCSS = ( sheet: VirtSheet, exports: CSSExports, assets: InterimAsset[] ): InterimCSS => { return { sheetText: stringifyCSSSheet(sheet, { resolveUrl(url) { return ( assets.find(asset => asset.originalPath === url)?.moduleContent || url ); } }), exports: { classNames: (Object as any).fromEntries( Object.keys(exports.classNames) .filter(className => { return exports.classNames[className].public; }) .map(className => { return [className, exports.classNames[className].scopedName]; }) ) } }; }; ================================================ FILE: packages/paperclip-interim/src/translate/html.ts ================================================ import { Node, getParts, Element, infer, EngineDelegate } from "@paperclip-ui/core"; import { AttributeKind, AttributeValue, AttributeValueKind, DynamicStringAttributeValuePartKind, getAttributeStringValue, getPartIds, getStyleScopeId, hasAttribute, NodeKind, Reference, Slot, Text } from "@paperclip-ui/utils"; import { InterimElement, InterimSlotNode, InterimComponent, InterimNode, InterimAttributeValue, InterimAttribute, InterimAttributeValuePart, InterimAttributeValuePartKind, InterimNodeKind, InterimText, InterimImport } from "../state"; import { translateScript } from "./script"; import { InterimCompilerOptions, ModuleContext } from "./options"; import { maybeEmbed } from "./utils"; import { InterimAsset } from "../state/assets"; export const translateComponents = ( ast: Node, filePath: string, engine: EngineDelegate, imports: InterimImport[], assets: InterimAsset[] ) => { const components = getParts(ast); const context: ModuleContext = { filePath, imports, assets, engine, componentNames: getPartIds(ast), scopeIds: [ ...getScopes(filePath), ...imports.filter(imp => imp.injectedStyles).map(imp => imp.publicScopeId) ] }; return components.map(component => translateComponent(component, context)); }; const getTagNameParts = (tagName: string) => { const parts = tagName.split("."); if (parts.length === 1) { return { tagName: parts[0] }; } return { innerTagName: parts[1], tagName: parts[0] }; }; const translateComponent = ( component: Element, context: ModuleContext ): InterimComponent => { const as = getAttributeStringValue("as", component); const { tagName, innerTagName } = getTagNameParts(component.tagName); return { id: component.id, tagName, as, innerTagName, schema: infer(component), isInstance: !isNativeElement(component.tagName, context), scopeClassNames: getScopeClassNames(component, context), exported: hasAttribute("export", component), range: component.range, kind: InterimNodeKind.Component, attributes: translateAttributes(component, context), children: translateChildren(component.children, context) }; }; const translateChildren = ( children: Node[], context: ModuleContext ): InterimNode[] => children.map(child => translateChild(child, context)).filter(Boolean); const translateChild = (node: Node, context: ModuleContext): InterimNode => { switch (node.nodeKind) { case NodeKind.Text: { return translateText(node); } case NodeKind.Element: { return translateElement(node, context); } case NodeKind.Slot: { return translateSlotNode(node, context); } } }; const getScopeClassNames = (element: Element, context: ModuleContext) => { const scopeIds = [`_${element.id}`]; if ( isNativeElement(element.tagName, context) || isImportedInstance(element.tagName, context) ) { scopeIds.push(...context.scopeIds); } return scopeIds; }; const translateText = (text: Text): InterimText => { return { value: text.value, kind: InterimNodeKind.Text, range: text.range }; }; const translateAttributes = (element: Element, context: ModuleContext) => { const groups: Record = {}; for (const attribute of element.attributes) { switch (attribute.attrKind) { case AttributeKind.KeyValueAttribute: { maybeAddAttributeValue( groups, attribute.name, { range: attribute.range, variantName: null, parts: getAttributeValueParts( attribute.value, attribute.name, element.tagName, context ) }, element.tagName, context ); break; } case AttributeKind.PropertyBoundAttribute: { maybeAddAttributeValue( groups, attribute.name, { range: attribute.range, variantName: attribute.bindingName, parts: getAttributeValueParts( attribute.value, attribute.name, element.tagName, context ) }, element.tagName, context ); break; } case AttributeKind.ShorthandAttribute: { maybeAddAttributeValue( groups, (attribute.reference as Reference).path[0].name, { range: attribute.range, variantName: null, parts: [ { kind: InterimAttributeValuePartKind.Shorthand, range: attribute.reference.range } ] }, element.tagName, context ); break; } default: { throw new Error( `Cannot handle attribute value kind ${attribute.attrKind}` ); } } } return groups; }; const getAttributeValueParts = ( value: AttributeValue, attrName: string, tagName: string, context: ModuleContext ): InterimAttributeValuePart[] => { if (!value) { return null; } const injectedScopes: string[] = []; if (value.attrValueKind === AttributeValueKind.DyanmicString) { for (const part of value.values) { if ( part.partKind === DynamicStringAttributeValuePartKind.ClassNamePierce ) { const publicScopeId = context.imports.find( imp => imp.namespace === part.className )?.publicScopeId; if (publicScopeId) { injectedScopes.push(publicScopeId); } } } } switch (value.attrValueKind) { case AttributeValueKind.DyanmicString: { return value.values.map(part => { switch (part.partKind) { case DynamicStringAttributeValuePartKind.ClassNamePierce: { return { kind: InterimAttributeValuePartKind.Static, value: pierceClassName(part.className, context), range: part.range }; } case DynamicStringAttributeValuePartKind.Literal: { return { kind: InterimAttributeValuePartKind.Static, value: translateValuePart( part.value, attrName, tagName, injectedScopes, context ), range: part.range }; } case DynamicStringAttributeValuePartKind.Slot: { return { kind: InterimAttributeValuePartKind.Dynamic, script: translateScript(part, context), range: part.range }; } } }); } case AttributeValueKind.String: { return [ { kind: InterimAttributeValuePartKind.Static, value: translateValuePart( value.value, attrName, tagName, injectedScopes, context ), range: value.range } ]; } case AttributeValueKind.Slot: { return [ { kind: InterimAttributeValuePartKind.Dynamic, script: translateScript(value.script, context), range: value.range } ]; } } }; const getScopes = (filePath: string) => { return [`_${getStyleScopeId(filePath)}`, `_pub-${getStyleScopeId(filePath)}`]; }; const translateValuePart = ( part: string, attrName: string, elementName: string, elementScopes: string[], context: ModuleContext ) => { if (!isNativeElement(elementName, context)) { return part; } const allScopes = [...elementScopes, ...context.scopeIds]; if (attrName === "class") { return part .split(" ") .map(slice => { if (!slice) { return ""; } return addScopeIds(slice, allScopes); }) .join(" "); } if (attrName === "src") { const asset = context.assets.find(asset => asset.originalPath === part); if (asset && asset.moduleContent) { return asset.moduleContent; } } return part; }; const pierceClassName = (className: string, context: ModuleContext) => { let scopeIds: string[] = context.scopeIds; const parts = className.split("."); if (parts.length === 2) { const imp = context.imports.find(imp => imp.namespace === parts[0]); if (imp) { scopeIds = getScopes(imp.filePath); } } return addScopeIds(parts[parts.length - 1], scopeIds); }; const addScopeIds = (className: string, scopeIds: string[]) => { return ( scopeIds.map(scopeId => `${scopeId}_${className}`).join(" ") + ` ${className}` ); }; const isNativeElement = (tagName: string, context: ModuleContext) => { return ( !isImportedInstance(tagName, context) && !context.componentNames.includes(tagName) ); }; const isImportedInstance = (tagName: string, context: ModuleContext) => { return ( tagName.includes(".") || context.imports.find(imp => imp.namespace === tagName) ); }; const maybeAddAttributeValue = ( groups: Record, name: string, value: InterimAttributeValue, tagName: string, context: ModuleContext ) => { // skip intermal props if (/^(export|component|as)$/.test(name)) { return; } const isNative = isNativeElement(tagName, context); if (!groups[name]) { groups[name] = { variants: [] }; } const group = groups[name]; // prohibit multiple attributes with the same variant name. E.g: `className="a" className="b"`. In this case, the last one wins. for (let i = group.variants.length; i--; ) { const variant = group.variants[i]; if (variant.variantName === value.variantName) { group.variants.splice(i, 1); break; } } group.variants.push(value); }; export const translateElement = ( element: Element, context: ModuleContext ): InterimElement => { const { tagName, innerTagName } = getTagNameParts(element.tagName); return { id: element.id, kind: InterimNodeKind.Element, innerTagName, tagName: tagName, isInstance: !isNativeElement(element.tagName, context), scopeClassNames: getScopeClassNames(element, context), attributes: translateAttributes(element, context), range: element.range, children: translateChildren(element.children, context) }; }; const translateSlotNode = ( slot: Slot, context: ModuleContext ): InterimSlotNode => { return { kind: InterimNodeKind.Slot, script: translateScript(slot.script, context), range: slot.range }; }; ================================================ FILE: packages/paperclip-interim/src/translate/index.ts ================================================ export * from "./module"; ================================================ FILE: packages/paperclip-interim/src/translate/module.ts ================================================ import { EngineDelegate, Node, VirtSheet, isErrorResult, } from "@paperclip-ui/core"; import * as path from "path"; import * as fs from "fs"; import * as URL from "url"; import { getAttributeStringValue, getImports, getStyleScopeId, hasAttribute, isNode, NodeKind, PCExports, isCSSFile, Module, getScopedCSSFilePath, traverseExpression, ModuleKind, CSSExports, isCSSExports, } from "@paperclip-ui/utils"; import { getAssets } from "./assets"; import { translateCSS } from "./css"; import { translateComponents } from "./html"; import { FIO, InterimCompilerOptions } from "./options"; import { InterimModule, InterimImport, InterimComponent } from "../state"; import { InterimAsset } from "../state/assets"; const castAsFilePath = (filePath: string) => { if (filePath.indexOf("file://") === 0) { filePath = URL.fileURLToPath(filePath); } return filePath; }; const defaultFIO: FIO = { readFile(filePath: string) { return fs.readFileSync(castAsFilePath(filePath)); }, getFileSize(filePath: string) { return fs.lstatSync(castAsFilePath(filePath)).size; }, }; export class InterimCompiler { readonly options: InterimCompilerOptions; constructor( private _engine: EngineDelegate, options: InterimCompilerOptions ) { this.options = { io: defaultFIO, ...options, }; } parseFile(filePath: string): InterimModule { const { sheet, exports } = this._engine.open(filePath); const ast = this._engine.parseFile(filePath); if (isErrorResult(ast)) { throw ast.error; } if (sheet.error) { throw sheet.error; } return translateInterim( ast, sheet, castAsFilePath(filePath), this._engine, exports, this.options ); } } const translateInterim = ( ast: Module, sheet: VirtSheet, filePath: string, engine: EngineDelegate, exports: PCExports | CSSExports, options: InterimCompilerOptions ): InterimModule => { let components: InterimComponent[] = []; let imports: InterimImport[] = []; let assets: InterimAsset[] = []; if (ast.moduleKind === ModuleKind.PC) { imports = translateImports(ast, filePath, engine, options); assets = getAssets(filePath, ast, sheet, engine, options); components = translateComponents(ast, filePath, engine, imports, assets); } else { assets = getAssets(filePath, null, sheet, engine, options); } return { imports, components, css: translateCSS( sheet, isCSSExports(exports) ? exports : exports.style, assets ), assets, }; }; const translateImports = ( ast: Node, filePath: string, engine: EngineDelegate, options: InterimCompilerOptions ): InterimImport[] => { return getImports(ast) .map((imp) => { if (!hasAttribute("src", imp)) { return null; } const src = getAttributeStringValue("src", imp); // do not include css if (isCSSFile(src) && !options.targetOptions.importAssetsAsModules) { return null; } let resolvedFilePath = castAsFilePath(engine.resolveFile(filePath, src)); if (isCSSFile(resolvedFilePath)) { resolvedFilePath = getScopedCSSFilePath(resolvedFilePath); } const usedTagNames: any = {}; const namespace = getAttributeStringValue("as", imp); traverseExpression(ast, null, (node) => { if ( isNode(node) && node.nodeKind === NodeKind.Element && node.tagName.includes(".") ) { const [tagNamespace, tagName] = node.tagName.split("."); if (tagNamespace === namespace) { usedTagNames[tagName] = 1; } } }); return { filePath: resolvedFilePath, relativePath: relative( path.relative(path.dirname(filePath), resolvedFilePath) ), publicScopeId: `_pub-${getStyleScopeId(resolvedFilePath)}`, namespace, usedTagNames: Object.keys(usedTagNames) as string[], injectedStyles: hasAttribute("inject-styles", imp), }; }) .filter(Boolean); }; const relative = (path: string) => path.charAt(0) === "." ? path : "./" + path; ================================================ FILE: packages/paperclip-interim/src/translate/options.ts ================================================ import { EngineDelegate } from "@paperclip-ui/core"; import { CompilerOptions, EngineDelegateChanged, PaperclipConfig, StringifySheetOptions } from "@paperclip-ui/utils"; import { InterimImport } from ".."; import { InterimAsset } from "../state/assets"; export type FIO = { readFile: (filePath: string) => Buffer; getFileSize: (filePath: string) => number; }; export type InterimCompilerOptions = { cwd: string; config: PaperclipConfig; targetOptions: CompilerOptions; io?: FIO; }; export type ModuleContext = { filePath: string; engine: EngineDelegate; assets: InterimAsset[]; componentNames: string[]; imports: InterimImport[]; scopeIds: string[]; }; ================================================ FILE: packages/paperclip-interim/src/translate/script.ts ================================================ import { Element, ScriptExpression, ScriptExpressionKind } from "@paperclip-ui/utils"; import { InterimConjunctionOperator, InterimScriptExpressionKind, InterimScriptExpression } from "../state/script"; import { translateElement } from "./html"; import { ModuleContext } from "./options"; export const translateScript = ( script: ScriptExpression, context: ModuleContext ): InterimScriptExpression => { switch (script.scriptKind) { case ScriptExpressionKind.Group: { return { kind: InterimScriptExpressionKind.Group, inner: translateScript(script.expression, context), range: script.range }; } case ScriptExpressionKind.Node: { return { kind: InterimScriptExpressionKind.Element, element: translateElement(script as Element, context), range: script.range }; } case ScriptExpressionKind.Not: { return { kind: InterimScriptExpressionKind.Not, expression: translateScript(script.expression, context), range: script.range }; } case ScriptExpressionKind.Number: { return { kind: InterimScriptExpressionKind.Number, value: String(script.value), range: script.range }; } case ScriptExpressionKind.Reference: { return { kind: InterimScriptExpressionKind.Reference, name: script.path[0].name, optional: script.path[0].optional, range: script.range }; } case ScriptExpressionKind.Boolean: { return { kind: InterimScriptExpressionKind.Boolean, value: script.value, range: script.range }; } case ScriptExpressionKind.String: { return { kind: InterimScriptExpressionKind.String, value: script.value, range: script.range }; } case ScriptExpressionKind.Conjunction: { return { kind: InterimScriptExpressionKind.Conjunction, operator: (script.operator as any) as InterimConjunctionOperator, left: translateScript(script.left, context), right: translateScript(script.right, context), range: script.range }; } } }; ================================================ FILE: packages/paperclip-interim/src/translate/utils.ts ================================================ import { EngineDelegate } from "@paperclip-ui/core"; import { FIO } from "./options"; export const maybeEmbed = ( fromPath: string, relPath: string, engine: EngineDelegate, maxSize: number, io: FIO ) => { const filePath = engine.resolveFile(fromPath, relPath); const fileSize = io.getFileSize(filePath); if (fileSize > maxSize) { return relPath; } return `EMBED`; }; ================================================ FILE: packages/paperclip-interim/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./lib", "target": "es5", "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true, "declaration": true, "strict": false /* Enable all strict type-checking options. */ }, "exclude": [ "**/*.d.ts", "lib/**/*.d.ts", "esm", "native", "lib/**/*", "node_modules", "*-test", "index.d.ts", "browser.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-language-service/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/paperclip-language-service/README.md ================================================ #### TODOS - engine handling to be done in another worker via MessageChannel ================================================ FILE: packages/paperclip-language-service/index.d.ts ================================================ export * from "./src"; ================================================ FILE: packages/paperclip-language-service/index.js ================================================ module.exports = require("./lib"); ================================================ FILE: packages/paperclip-language-service/package.json ================================================ { "name": "@paperclip-ui/language-service", "version": "18.5.6", "description": "", "main": "index.js", "publishConfig": { "access": "public" }, "scripts": { "build": "tsc", "build:watch": "tsc --watch --preserveWatchOutput --incremental", "test": "mocha lib/**/*-test.js" }, "author": "", "license": "ISC", "dependencies": { "@paperclip-ui/core": "^18.5.6", "@paperclip-ui/test-utils": "^18.5.2", "@paperclip-ui/utils": "^18.5.6", "color": "^4.0.0", "typescript": "^4.2.3" }, "devDependencies": { "@types/mocha": "^9.1.0", "chai": "^4.3.4", "mocha": "^9.0.3", "typescript": "^4.2.3" }, "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a" } ================================================ FILE: packages/paperclip-language-service/src/autocomplete.ts ================================================ // super helpful: https://github.com/microsoft/vscode-extension-samples/blob/master/completions-sample/src/extension.ts import * as fs from "fs"; import { getSuggestionContext, SuggestContextKind, HTMLAttributeStringValueContext, HTMLAttributeNameSuggestionContext, CSSDeclarationValueSuggestionContext, CSSDeclarationSuggestionContext, CSSDeclarationAtRuleSuggestionContext, CSSDeclarationAtRuleParamsSuggestionContext, CSSAtRuleSuggestionContext, CSSFunctionSuggestionContext, HTMLCloseTagNameSuggestionContext, } from "@paperclip-ui/autocomplete"; import { resolveAllPaperclipFiles, resolveAllAssetFiles, } from "@paperclip-ui/core"; import { ComponentExport, EvaluatedDataKind, LoadedPCData, } from "@paperclip-ui/utils"; import { stringArrayToAutoCompleteItems, addCompletionItemData, PCCompletionItem, CompletionItem, getStyleExport, RETRIGGER_COMMAND, tagCompletionItem, InsertTextFormat, } from "./utils"; import { CSS_DECLARATION_VALUE_COMPLETION_ITEMS, CSS_DECLARATION_NAME_COMPLETION_ITEMS, ATTRIBUTE_NAME_COMPLETION_ITEMS, TAG_NAME_COMPLETION_ITEMS, AT_RULE_COMPLETION_ITEMS, } from "./completion-items"; import { DEFAULT_PART_ID } from "@paperclip-ui/core"; import { LoadedData } from "@paperclip-ui/core"; import { memoize } from "lodash"; import { LoadedDataDetails } from "@paperclip-ui/core/src/core/delegate"; const EMPTY_ARRAY = []; export class AutocompleteService { constructor(private _fs: any = fs) {} resolveCompletionItem(item: PCCompletionItem): CompletionItem { return item; } getSuggestions( uri: string, text: string, data: LoadedData, imports: Record ): PCCompletionItem[] { return this.getSuggestions2(uri, text, data, imports).map((item) => addCompletionItemData(item, uri) ); } private getSuggestions2( uri: string, text: string, data: LoadedData | null, imports: Record ): CompletionItem[] { let context; // some lingering bugs, so try & catch them try { context = getSuggestionContext(text); } catch (e) { // more detailed when we log it out like this console.error(e.stack); throw e; } if (!context) { return []; } try { switch (context.kind) { case SuggestContextKind.HTML_TAG_NAME: return this._getHTMLTagNameSuggestions(data as LoadedPCData, imports); case SuggestContextKind.HTML_ATTRIBUTE_NAME: return this._getAttributeNameSuggestions( context, data as LoadedPCData, imports ); case SuggestContextKind.HTML_STRING_ATTRIBUTE_VALUE: return this._getHTMLAttributeStringValueSuggestions( uri, context, data, imports ); case SuggestContextKind.CSS_DECLARATION_NAME: return this._getCSSDeclarationNameSuggestion(context, data, imports); case SuggestContextKind.CSS_DECLARATION_AT_RULE: return this._getCSSDeclarationAtRuleSuggestion( context, data, imports ); case SuggestContextKind.CSS_AT_RULE_PARAMS: return this._getCSSDeclarationAtRuleParamsSuggestion( context, data, imports ); case SuggestContextKind.CSS_AT_RULE_NAME: return this._getCSSAtRuleSuggestion(context); case SuggestContextKind.CSS_DECLARATION_VALUE: return this._getCSSDeclarationValueSugestion(context, data, imports); case SuggestContextKind.CSS_FUNCTION: return this._getCSSFunctionSuggestion(context, uri, data, imports); case SuggestContextKind.CSS_CLASS_REFERENCE: return this._getCSSClassReferenceSuggestion(data, imports); } } catch (e) { console.error(e.stack); throw e; } return []; } // slightly buggy, so don't do for now private _getCloseTagSuggestion( context: HTMLCloseTagNameSuggestionContext ): CompletionItem[] { return [ { label: ``, preselect: true, insertText: `\n\t$0\n`, insertTextFormat: InsertTextFormat.Snippet, commitCharacters: [">"], }, ]; } private _getHTMLTagNameSuggestions( data: LoadedPCData, imports: Record ) { return getHTMLTagNameSuggestions(data, imports); } private _getCSSDeclarationAtRuleSuggestion( _context: CSSDeclarationAtRuleSuggestionContext, data: LoadedData, imports: Record ) { return [ { label: "include", insertText: "include ${1:};", insertTextFormat: InsertTextFormat.Snippet, command: loadedMixinsAsCompletionList(data, imports).length ? RETRIGGER_COMMAND : null, }, { label: "content", insertText: "content;", }, ]; } private _getCSSAtRuleSuggestion(_context: CSSAtRuleSuggestionContext) { return AT_RULE_COMPLETION_ITEMS; } private _getCSSDeclarationAtRuleParamsSuggestion( context: CSSDeclarationAtRuleParamsSuggestionContext, data: LoadedData, imports: Record ) { if (context.atRuleName === "include") { return loadedMixinsAsCompletionList(data, imports); } else { return []; } } private _getComponentPropCompletionItems(componentInfo: ComponentExport) { return Object.keys(componentInfo.properties).map( (propertyName) => ({ label: propertyName, // slightly opinionated, but okay, I think. insertText: /^on[A-Z]/.test(propertyName) ? `${propertyName}={\${1:}}` : propertyName, insertTextFormat: InsertTextFormat.Snippet, } as CompletionItem) ); } private _getAttributeNameSuggestions( context: HTMLAttributeNameSuggestionContext, data: LoadedPCData, imports: Record ) { const items: CompletionItem[] = []; const basename = context.tagPath[0]; let isComponent; if (data) { if (data.exports.components[basename]) { const componentInfo = data.exports.components[basename]; items.push(...this._getComponentPropCompletionItems(componentInfo)); isComponent = true; } else if (imports[basename]?.kind === EvaluatedDataKind.PC) { const componentAs = context.tagPath[1] || DEFAULT_PART_ID; const compInfo = (imports[basename] as LoadedPCData).exports.components[ componentAs ]; items.push(...this._getComponentPropCompletionItems(compInfo)); isComponent = true; } } if (!isComponent && context.tagPath.length === 1) { items.push( ...(ATTRIBUTE_NAME_COMPLETION_ITEMS[context.tagPath[0]] || []).map( (item) => { if (item.label === "class" && containsClasses(data, imports)) { return { ...item, command: RETRIGGER_COMMAND, }; } return item; } ) ); } return items; } private _getHTMLAttributeStringValueSuggestions( uri: string, context: HTMLAttributeStringValueContext, data: LoadedData, imports: Record ) { if (context.tagPath.length === 1 && context.tagPath[0] === "import") { if (context.attributeName == "src") { return stringArrayToAutoCompleteItems( resolveAllPaperclipFiles(this._fs)(uri, true) ); } } if (context.attributeName === "src") { return stringArrayToAutoCompleteItems( resolveAllAssetFiles(this._fs)(uri, true) ); } const attrName = context.attributeName.split(":").shift(); if (attrName === "class") { return this._getCSSClassReferenceSuggestion(data, imports); } return []; } private _getCSSDeclarationValueSugestion( info: CSSDeclarationValueSuggestionContext, data: LoadedData, imports: Record ) { const styleExports = data ? data.kind === EvaluatedDataKind.PC ? data.exports.style : data.exports : null; // This is the easiest approach. Ignore if there's text right before cursor -- this is to prevent bad autocompletions. E.g // -- expanded to --var(--color) if ( info.declarationValuePrefix && info.declarationValuePrefix.charAt( info.declarationValuePrefix.length - 1 ) !== " " ) { return []; } const list = [ ...(CSS_DECLARATION_VALUE_COMPLETION_ITEMS[info.declarationName] || EMPTY_ARRAY), ]; if ( info.declarationName === "animation" || (info.declarationName === "animation-name" && styleExports) ) { for (const name in styleExports.keyframes) { const info = styleExports.keyframes[name]; list.push({ label: info.name, }); } for (const id in imports) { if (/\//.test(id)) { continue; } const imp = imports[id]; const styleExport = getStyleExport(imp); for (const name in styleExport.keyframes) { const info = styleExport.keyframes[name]; if (!info.public) { continue; } list.push({ label: `${id}.${name}`, }); } } } list.push(...declaredVarsToCompletionItems(data, imports, true)); return list; } private _getCSSFunctionSuggestion( context: CSSFunctionSuggestionContext, uri: string, data: LoadedData, imports: Record ) { if (context.name === "var") { return declaredVarsToCompletionItems(data, imports); } if (context.name === "url") { return stringArrayToAutoCompleteItems( resolveAllAssetFiles(this._fs)(uri, true) ); } return []; } private _getCSSClassReferenceSuggestion( data: LoadedData, imports: Record, includeImports = true ) { const list: CompletionItem[] = []; const styleExports = data ? data.kind === EvaluatedDataKind.PC ? data.exports.style : data.exports : null; for (const className in styleExports?.classNames) { list.push({ label: className, }); } if (includeImports) { for (const id in imports) { const details = imports[id]; if (/\//.test(id) && !details.injectStyles) { continue; } const cx = getStyleExport(imports[id]).classNames; for (const className in cx) { const part = cx[className]; if (!part.public) { continue; } if (details.injectStyles) { list.push({ label: className, }); } else { list.push({ label: `${id}.${className}`, }); } } } } return list; } // TODO - possibly take evaluated CSS rule into consideration, and filter options based on sibling // declarations. Should be possible since virtual styles contain positions, so we just need to find on that // corresponds to the caret position. private _getCSSDeclarationNameSuggestion( info: CSSDeclarationSuggestionContext, data: LoadedData, imports: Record ) { let list = CSS_DECLARATION_NAME_COMPLETION_ITEMS; if (containsVars(data, imports)) { list = list.map((item) => ({ ...item, command: RETRIGGER_COMMAND })); } return list; } } export const getHTMLTagNameSuggestions = ( data: LoadedPCData, imports: Record ) => { const options = []; if (data) { for (const tagName in data.exports.components) { const componentInfo = data.exports.components[tagName]; options.push( tagCompletionItem( tagName, Object.keys(componentInfo.properties).length > 0 ) ); } for (const id in imports) { if (/\//.test(id)) { continue; } const imp = imports[id]; if (imp.kind === EvaluatedDataKind.PC) { for (const componentId in imp.exports.components) { const componentInfo = imp.exports.components[componentId]; if (!componentInfo || !componentInfo.public) { continue; } let tagName; if (componentId === DEFAULT_PART_ID) { tagName = id; } else { tagName = `${id}.${componentId}`; } options.push( tagCompletionItem( tagName, Object.keys(componentInfo.properties).length > 0 ) ); } } } } options.push(...TAG_NAME_COMPLETION_ITEMS); return options; }; const declaredVarsToCompletionItems = memoize( ( data: LoadedData, imports: Record, includeVar?: boolean ) => { const styleExports = getStyleExport(data); const list: CompletionItem[] = []; const used = {}; for (const name in styleExports?.variables) { used[name] = true; list.push({ label: name, insertText: includeVar ? `var(${name})` : name, sortText: "zz" + name, detail: styleExports.variables[name].value, }); } for (const imp in imports) { const styleExports = getStyleExport(imports[imp]); for (const name in styleExports?.variables) { if (used[name]) { continue; } used[name] = true; list.push({ label: name, insertText: includeVar ? `var(${name})` : name, sortText: "zz" + name, detail: styleExports.variables[name].value, }); } } return list.sort((a, b) => (a.label > b.label ? -1 : 1)); } ); const containsVars = ( data: LoadedData, imports: Record ) => { return containsExports(data, imports, "variables"); }; const containsClasses = ( data: LoadedPCData, imports: Record ) => { return containsExports(data, imports, "classNames"); }; const containsExports = ( data: LoadedData, imports: Record, kind: "classNames" | "keyframes" | "variables" ) => { const styleExports = getStyleExport(data); if (styleExports) for (const name in styleExports[kind]) { return true; } for (const imp in imports) { const exp = getStyleExport(imports[imp]); if (exp) for (const name in exp[kind]) { return true; } } return false; }; const loadedMixinsAsCompletionList = memoize( (data: LoadedData, imports: Record) => { const list: CompletionItem[] = []; const styleExports = getStyleExport(data); for (const mixinName in styleExports.mixins) { // const mixin = data.exports.style.mixins[mixinName]; list.push({ label: mixinName, // detail: stringifyDeclarations(mixin.declarations) }); } for (const importId in imports) { // is file if (/\//.test(importId)) { continue; } const imp = getStyleExport(imports[importId]); for (const mixinName in imp.mixins) { const mixin = imp.mixins[mixinName]; if (!mixin.public) { continue; } list.push({ label: `${importId}.${mixinName}`, // detail: stringifyDeclarations(mixin.declarations) }); } } return list; } ); ================================================ FILE: packages/paperclip-language-service/src/collect-ast-info.ts ================================================ import { Sheet, LoadedData, memoize, StringRange, traverseExpression, DependencyContent, Node, NodeKind, traverseSheet, Element, isStyleDeclaration, KeyValueDeclaration, StyleDeclarationKind, DependencyContentKind, DependencyGraph, isComponent, isRule, RuleKind, getInstanceComponentInfo, getComponentMap, getDocumentComponents, MixinRule, KeyframesRule, getChildren, isImport, DependencyNodeContent, getAttributeValue, getAttributeStringValue, AttributeValueKind, } from "@paperclip-ui/utils"; import { CSS_COLOR_NAME_REGEXP } from "./css-color-names"; import * as parseColor from "color"; type Color = { red: number; green: number; blue: number; alpha: number; }; export type ColorInfo = { value: Color; start: number; end: number; }; type DocumentLinkInfo = { uri: string; range: StringRange; }; export type DefinitionInfo = { sourceUri: string; instanceRange: StringRange; sourceRange: StringRange; sourceDefinitionRange: StringRange; }; type CollectASTInfoResult = { colors: ColorInfo[]; links: DocumentLinkInfo[]; definitions: DefinitionInfo[]; }; const EMPTY: CollectASTInfoResult = { colors: [], links: [], definitions: [], }; export const collectASTInfo = ( entryUri: string, graph: DependencyGraph, evaluated: Record ): CollectASTInfoResult => { if (!graph[entryUri]) { return EMPTY; } try { const map = { colors: getDocumentColors(entryUri, graph), links: getDocumentLinks(entryUri, graph), definitions: getDocumentDefinitions(entryUri, graph), }; return map; } catch (e) { console.error(e); return EMPTY; } }; const getDocumentColors = (uri: string, asts: DependencyGraph) => { const documentAST = asts[uri]; const { declarations } = getDocumentASTInfo(documentAST.content); const colors: ColorInfo[] = []; const allVariables = getAllDocumentVariables(uri, asts); for (const declaration of declarations) { addDeclarationColors(declaration, colors, allVariables); } return colors; }; const getDocumentLinks = ( uri: string, asts: DependencyGraph ): DocumentLinkInfo[] => { const documentAST = asts[uri]; if (documentAST.content.contentKind !== DependencyContentKind.Node) { return []; } const links: DocumentLinkInfo[] = []; const imports = getImportMap(documentAST.content); for (const importId in imports) { const imp = imports[importId]; const src = getAttributeValue("src", imp); if (src?.attrValueKind !== AttributeValueKind.String) { continue; } links.push({ uri: asts[uri].dependencyUriMaps[src.value], range: src.range, }); } return links; }; const getDocumentDefinitions = (uri: string, graph: DependencyGraph) => { const dependency = graph[uri]; if (dependency.content.contentKind !== DependencyContentKind.Node) { return; } const definitions: DefinitionInfo[] = []; const { instances } = getNodeASTInfo(dependency.content); for (const instance of instances) { const [instanceUri, component] = getInstanceComponentInfo( instance, uri, graph ); if (component) { definitions.push({ sourceUri: instanceUri, sourceRange: component.range, sourceDefinitionRange: component.range, instanceRange: instance.tagNameRange, }); } } return definitions; }; const getAllDocumentVariables = (uri: string, graph: DependencyGraph) => { const entry = graph[uri]; const allVariables: Record = { ...getDocumentVariables(entry.content), }; for (const relPath in entry.dependencyUriMaps) { const dep = graph[entry.dependencyUriMaps[relPath]]; if (!dep) { continue; } Object.assign( allVariables, getDocumentVariables(dep.content), allVariables ); } return allVariables; }; const getDocumentVariables = memoize((data: DependencyContent) => { const { declarations } = data.contentKind === DependencyContentKind.Node ? getNodeASTInfo(data) : getSheetASTInfo(data); const variables: Record = {}; for (const decl of declarations) { if (decl.name.startsWith("--")) { variables[decl.name] = decl; } } return variables; }); const collectStyleSheetInfo = (ast: Sheet, colors: ColorInfo[]) => { const { declarations } = getSheetASTInfo(ast); }; const getSheetASTInfo = memoize((ast: Sheet) => { const declarations: KeyValueDeclaration[] = []; const keyframes: KeyframesRule[] = []; const mixins: MixinRule[] = []; traverseSheet(ast, null, (expr) => { if ( isStyleDeclaration(expr) && expr.declarationKind === StyleDeclarationKind.KeyValue ) { declarations.push(expr); } if (isRule(expr)) { if (expr.ruleKind === RuleKind.Keyframes) { keyframes.push(expr); } else if (expr.ruleKind === RuleKind.Mixin) { mixins.push(expr); } } }); return { declarations, keyframes, mixins }; }); const getDocumentASTInfo = (content: DependencyContent) => content.contentKind === DependencyContentKind.Node ? getNodeASTInfo(content) : getSheetASTInfo(content); const getPCDocumentImports = (content: Node) => { return getChildren(content).filter(isImport) as Element[]; }; const getImportMap = memoize( (root: Node): Record => getPCDocumentImports(root).reduce((map, element) => { map[getAttributeStringValue("as", element)] = element; return map; }, {}) ); const getNodeASTInfo = memoize((root: Node) => { const allDecls: KeyValueDeclaration[] = []; const allComponents: Element[] = []; const allKeyframes: KeyframesRule[] = []; const allMixins: MixinRule[] = []; const instances: Element[] = []; const components = getComponentMap(root); const imports = getImportMap(root); traverseExpression(root, null, (node: Node) => { if (node.nodeKind === NodeKind.StyleElement) { const { declarations, mixins, keyframes } = getSheetASTInfo(node.sheet); allDecls.push(...declarations); allMixins.push(...mixins); allKeyframes.push(...keyframes); } if (isComponent(node)) { allComponents.push(node); } if (node.nodeKind === NodeKind.Element) { if (components[node.tagName]) { instances.push(node); } else { const nameParts = node.tagName.split("."); if (imports[nameParts[0]]) { instances.push(node); } } } }); return { declarations: allDecls, components: allComponents, keyframes: allKeyframes, mixins: allMixins, instances, }; }); const addDeclarationColors = ( decl: KeyValueDeclaration, allColors: ColorInfo[], variables: Record ) => { const colors = matchColor(decl.value) || decl.value.match(/#[^\s,;]+|(var)\(.*?\)/g) || []; let modelDecl = decl.value; for (const color of colors) { let colorValue; if (/var\(.*?\)/.test(color)) { const name = color.match(/var\((.*?)\)/)[1]; const varDecl = variables[name]; if (varDecl) { const match = matchColor(varDecl.value); if (match) { colorValue = match[0]; } } } else { colorValue = color; } if (!colorValue) { continue; } const colorIndex = modelDecl.indexOf(color); // ensure that color isn't there in case there is another instance // in the string -- want to go through each one. modelDecl = modelDecl.replace(color, "_".repeat(color.length)); const colorStart = decl.valueRange.start.pos + colorIndex; try { const { color: [red, green, blue], valpha: alpha, } = parseColor(colorValue); allColors.push({ value: { red: red / 255, green: green / 255, blue: blue / 255, alpha }, start: colorStart, end: colorStart + color.length, }); } catch (e) {} } }; const matchColor = (value: string) => { return ( value.match(/#[a-zA-Z0-9]+|(rgba|rgb|hsl|hsla|var)\(.*?\)/g) || value.match(CSS_COLOR_NAME_REGEXP) ); }; ================================================ FILE: packages/paperclip-language-service/src/completion-items.ts ================================================ import { stringArrayToAutoCompleteItems, RETRIGGER_COMMAND, tagCompletionItem, InsertTextFormat, CompletionItem, CompletionItemKind } from "./utils"; import { CSS_DECLARATION_NAMES, CSS_DECLARATION_VALUE_MAP, CSS_AT_RULE_NAMES } from "./css-decl-value-constants"; import { ELEMENT_ATTRIBUTES, ALL_TAG_NAMES } from "./tag-name-constants"; import { merge } from "lodash"; const ATTRIBUTE_NAME_COMPLETION_ITEMS = {}; for (const tagName in ELEMENT_ATTRIBUTES) { ATTRIBUTE_NAME_COMPLETION_ITEMS[tagName] = []; for (const attrName of ELEMENT_ATTRIBUTES[tagName]) { let item: CompletionItem = { label: attrName }; if (tagName === "import" && attrName === "src") { item = { ...item, insertText: `src="$1"`, insertTextFormat: InsertTextFormat.Snippet, command: RETRIGGER_COMMAND }; } else if (attrName === "as" || attrName === "class") { item = { ...item, insertText: `${attrName}="$1"`, insertTextFormat: InsertTextFormat.Snippet }; } ATTRIBUTE_NAME_COMPLETION_ITEMS[tagName].push(item); } } const TAG_NAME_COMPLETION_ITEMS = []; for (const tagName of ALL_TAG_NAMES) { const item = tagCompletionItem( tagName, ATTRIBUTE_NAME_COMPLETION_ITEMS[tagName].length > 0 ); TAG_NAME_COMPLETION_ITEMS.push(item); } const AT_RULE_COMPLETION_ITEMS = []; for (const name of CSS_AT_RULE_NAMES) { AT_RULE_COMPLETION_ITEMS.push({ label: name, insertText: `${name} `, insertTextFormat: InsertTextFormat.Snippet }); } /** * CSS Declaration name completion items */ const CSS_DECLARATION_NAME_COMPLETION_ITEMS = stringArrayToAutoCompleteItems( CSS_DECLARATION_NAMES ).map( ({ label }): CompletionItem => { if (label === "box-shadow") { return { label: "box-shadow", insertText: "box-shadow: ${1:offset-x} ${2:offset-y} ${3:blur} ${4:spread} ${5:color};", insertTextFormat: InsertTextFormat.Snippet }; } return { label, kind: CompletionItemKind.Property, insertText: label + ": ${1:};", insertTextFormat: InsertTextFormat.Snippet, command: CSS_DECLARATION_VALUE_MAP[label]?.length ? RETRIGGER_COMMAND : null, data: { cssDeclarationName: label } }; } ); CSS_DECLARATION_NAME_COMPLETION_ITEMS.push({ label: "box-shadow inset", insertText: "box-shadow: inset ${1:offset-x} ${2:offset-y} ${3:blur} ${4:spread} ${5:color};", insertTextFormat: InsertTextFormat.Snippet }); /** * CSS Declaration value completion items */ let CSS_DECLARATION_VALUE_COMPLETION_ITEMS: Record< string, CompletionItem[] > = {}; for (const name in CSS_DECLARATION_VALUE_MAP) { const values = CSS_DECLARATION_VALUE_MAP[name]; CSS_DECLARATION_VALUE_COMPLETION_ITEMS[name] = values.map( (value): CompletionItem => ({ label: value, insertText: value }) ); } const URL_COMPLETION_ITEM: CompletionItem = { label: "url", detail: "url()", insertText: "url($1)", insertTextFormat: InsertTextFormat.Snippet, command: RETRIGGER_COMMAND }; const ANIMATION_TIMING_FUNCTION_COMPLETION_ITEMS: CompletionItem[] = [ { label: "steps", detail: "steps(int, start|end)", insertText: "steps($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "cubic-bezier", detail: "cubic-bezier(n, n, n, n)", insertText: "cubic-bezier($1)", insertTextFormat: InsertTextFormat.Snippet } ]; const BACKGROUND_IMAGE_COMPLETION_ITEMS: CompletionItem[] = [ URL_COMPLETION_ITEM, { label: "linear-gradient", detail: "linear-gradient()", insertText: "linear-gradient($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "radial-gradient", detail: "radial-gradient()", insertText: "radial-gradient($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "repeating-linear-gradient", detail: "repeating-linear-gradient()", insertText: "repeating-linear-gradient($1)", insertTextFormat: InsertTextFormat.Snippet } ]; const CONTENT_COMPLETION_ITEMS: CompletionItem[] = [ URL_COMPLETION_ITEM, { label: "attr", detail: "attr(attribute)", insertText: "attr($1)", insertTextFormat: InsertTextFormat.Snippet } ]; const GRID_AUTO_COLUMNS_COMPLETION_ITEMS: CompletionItem[] = [ { label: "fit-content", detail: "fit-content(unit)", insertText: "fit-content($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "minmax", detail: "minmax(min, max)", insertText: "minmax(${1:min}, ${2:max})", insertTextFormat: InsertTextFormat.Snippet } ]; const FILTER_COMPLETION_ITEMS: CompletionItem[] = [ URL_COMPLETION_ITEM, { label: "blur", detail: "blur(px)", insertText: "blur($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "brightness", detail: "brightness(%)", insertText: "brightness($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "contrast", detail: "contrast(%)", insertText: "contrast($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "drop-shadow", detail: "drop-shadow()", insertText: "drop-shadow(${1:h-shadow} ${2:v-shadow} ${3:blur} ${5:color})", insertTextFormat: InsertTextFormat.Snippet }, { label: "grayscale", detail: "grayscale(%)", insertText: "grayscale($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "hue-rotate", detail: "hue-rotate(deg)", insertText: "hue-rotate($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "invert", detail: "invert(%)", insertText: "invert($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "opacity", detail: "opacity(%)", insertText: "opacity($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "saturate", detail: "saturate(%)", insertText: "saturate($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "sepia", detail: "sepia(%)", insertText: "sepia($1)", insertTextFormat: InsertTextFormat.Snippet } ]; const TRANSFORM_COMPLETION_ITEMS: CompletionItem[] = [ { label: "matrix3d", detail: "matrix3d()", insertText: "matrix3d($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "translate", detail: "translate()", insertText: "translate(${1:x}, ${2:y})", insertTextFormat: InsertTextFormat.Snippet }, { label: "translate3d", detail: "translate3d()", insertText: "translate3d(${1:x}, ${2:y}, ${2:z})", insertTextFormat: InsertTextFormat.Snippet }, { label: "translateX", detail: "translateX()", insertText: "translateX(${1:x})", insertTextFormat: InsertTextFormat.Snippet }, { label: "translateY", detail: "translateY()", insertText: "translateY(${1:y})", insertTextFormat: InsertTextFormat.Snippet }, { label: "translateZ", detail: "translateZ()", insertText: "translateZ(${1:z})", insertTextFormat: InsertTextFormat.Snippet }, { label: "scale", detail: "scale()", insertText: "scale($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "scale3d", detail: "scale3d()", insertText: "scale3d(${1:x}, ${2:y}, ${2:z})", insertTextFormat: InsertTextFormat.Snippet }, { label: "scaleX", detail: "scaleX()", insertText: "scaleX(${1:x})", insertTextFormat: InsertTextFormat.Snippet }, { label: "scaleY", detail: "scaleY()", insertText: "scaleY(${1:y})", insertTextFormat: InsertTextFormat.Snippet }, { label: "scaleZ", detail: "scaleZ()", insertText: "scaleZ(${1:z})", insertTextFormat: InsertTextFormat.Snippet }, { label: "rotate", detail: "rotate()", insertText: "rotate($1)", insertTextFormat: InsertTextFormat.Snippet }, { label: "rotate3d", detail: "rotate3d()", insertText: "rotate3d(${1:x}, ${2:y}, ${2:z})", insertTextFormat: InsertTextFormat.Snippet }, { label: "rotateX", detail: "rotateX()", insertText: "rotateX(${1:x})", insertTextFormat: InsertTextFormat.Snippet }, { label: "rotateY", detail: "rotateY()", insertText: "rotateY(${1:y})", insertTextFormat: InsertTextFormat.Snippet }, { label: "rotateZ", detail: "rotateZ()", insertText: "rotateZ(${1:z})", insertTextFormat: InsertTextFormat.Snippet }, { label: "skew", detail: "skew()", insertText: "skew(${1:x}, ${1:y})", insertTextFormat: InsertTextFormat.Snippet }, { label: "skew", detail: "skewX()", insertText: "skewX(${1:x})", insertTextFormat: InsertTextFormat.Snippet }, { label: "skewY", detail: "skewY()", insertText: "skewY(${1:y})", insertTextFormat: InsertTextFormat.Snippet }, { label: "perspective", detail: "perspective()", insertText: "perspective($1)", insertTextFormat: InsertTextFormat.Snippet } ]; // TODO: https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path const CLIP_PATH_COMPLETION_ITEMS: CompletionItem[] = []; CSS_DECLARATION_VALUE_COMPLETION_ITEMS = merge( {}, CSS_DECLARATION_VALUE_COMPLETION_ITEMS, { "clip-path": [...CLIP_PATH_COMPLETION_ITEMS], "transition-timing-function": [ ...ANIMATION_TIMING_FUNCTION_COMPLETION_ITEMS ], "animation-timing-function": [ ...ANIMATION_TIMING_FUNCTION_COMPLETION_ITEMS ], animation: [...ANIMATION_TIMING_FUNCTION_COMPLETION_ITEMS], "background-image": [...BACKGROUND_IMAGE_COMPLETION_ITEMS], "mask-image": [...BACKGROUND_IMAGE_COMPLETION_ITEMS], background: [...BACKGROUND_IMAGE_COMPLETION_ITEMS], mask: [...BACKGROUND_IMAGE_COMPLETION_ITEMS], content: [...CONTENT_COMPLETION_ITEMS], filter: [...FILTER_COMPLETION_ITEMS], "grid-auto-columns": [...GRID_AUTO_COLUMNS_COMPLETION_ITEMS], transform: [...TRANSFORM_COMPLETION_ITEMS] } ); export { CSS_DECLARATION_VALUE_COMPLETION_ITEMS, AT_RULE_COMPLETION_ITEMS, CSS_DECLARATION_NAME_COMPLETION_ITEMS, ATTRIBUTE_NAME_COMPLETION_ITEMS, TAG_NAME_COMPLETION_ITEMS }; ================================================ FILE: packages/paperclip-language-service/src/css-color-names.ts ================================================ export const CSS_COLORS = { aliceblue: "#f0f8ff", antiquewhite: "#faebd7", aqua: "#00ffff", aquamarine: "#7fffd4", azure: "#f0ffff", beige: "#f5f5dc", bisque: "#ffe4c4", black: "#000000", blanchedalmond: "#ffebcd", blue: "#0000ff", blueviolet: "#8a2be2", brown: "#a52a2a", burlywood: "#deb887", cadetblue: "#5f9ea0", chartreuse: "#7fff00", chocolate: "#d2691e", coral: "#ff7f50", cornflowerblue: "#6495ed", cornsilk: "#fff8dc", crimson: "#dc143c", cyan: "#00ffff", darkblue: "#00008b", darkcyan: "#008b8b", darkgoldenrod: "#b8860b", darkgray: "#a9a9a9", darkgreen: "#006400", darkgrey: "#a9a9a9", darkkhaki: "#bdb76b", darkmagenta: "#8b008b", darkolivegreen: "#556b2f", darkorange: "#ff8c00", darkorchid: "#9932cc", darkred: "#8b0000", darksalmon: "#e9967a", darkseagreen: "#8fbc8f", darkslateblue: "#483d8b", darkslategray: "#2f4f4f", darkslategrey: "#2f4f4f", darkturquoise: "#00ced1", darkviolet: "#9400d3", deeppink: "#ff1493", deepskyblue: "#00bfff", dimgray: "#696969", dimgrey: "#696969", dodgerblue: "#1e90ff", firebrick: "#b22222", floralwhite: "#fffaf0", forestgreen: "#228b22", fuchsia: "#ff00ff", gainsboro: "#dcdcdc", ghostwhite: "#f8f8ff", goldenrod: "#daa520", gold: "#ffd700", gray: "#808080", green: "#008000", greenyellow: "#adff2f", grey: "#808080", honeydew: "#f0fff0", hotpink: "#ff69b4", indianred: "#cd5c5c", indigo: "#4b0082", ivory: "#fffff0", khaki: "#f0e68c", lavenderblush: "#fff0f5", lavender: "#e6e6fa", lawngreen: "#7cfc00", lemonchiffon: "#fffacd", lightblue: "#add8e6", lightcoral: "#f08080", lightcyan: "#e0ffff", lightgoldenrodyellow: "#fafad2", lightgray: "#d3d3d3", lightgreen: "#90ee90", lightgrey: "#d3d3d3", lightpink: "#ffb6c1", lightsalmon: "#ffa07a", lightseagreen: "#20b2aa", lightskyblue: "#87cefa", lightslategray: "#778899", lightslategrey: "#778899", lightsteelblue: "#b0c4de", lightyellow: "#ffffe0", lime: "#00ff00", limegreen: "#32cd32", linen: "#faf0e6", magenta: "#ff00ff", maroon: "#800000", mediumaquamarine: "#66cdaa", mediumblue: "#0000cd", mediumorchid: "#ba55d3", mediumpurple: "#9370db", mediumseagreen: "#3cb371", mediumslateblue: "#7b68ee", mediumspringgreen: "#00fa9a", mediumturquoise: "#48d1cc", mediumvioletred: "#c71585", midnightblue: "#191970", mintcream: "#f5fffa", mistyrose: "#ffe4e1", moccasin: "#ffe4b5", navajowhite: "#ffdead", navy: "#000080", oldlace: "#fdf5e6", olive: "#808000", olivedrab: "#6b8e23", orange: "#ffa500", orangered: "#ff4500", orchid: "#da70d6", palegoldenrod: "#eee8aa", palegreen: "#98fb98", paleturquoise: "#afeeee", palevioletred: "#db7093", papayawhip: "#ffefd5", peachpuff: "#ffdab9", peru: "#cd853f", pink: "#ffc0cb", plum: "#dda0dd", powderblue: "#b0e0e6", purple: "#800080", rebeccapurple: "#663399", red: "#ff0000", rosybrown: "#bc8f8f", royalblue: "#4169e1", saddlebrown: "#8b4513", salmon: "#fa8072", sandybrown: "#f4a460", seagreen: "#2e8b57", seashell: "#fff5ee", sienna: "#a0522d", silver: "#c0c0c0", skyblue: "#87ceeb", slateblue: "#6a5acd", slategray: "#708090", slategrey: "#708090", snow: "#fffafa", springgreen: "#00ff7f", steelblue: "#4682b4", tan: "#d2b48c", teal: "#008080", thistle: "#d8bfd8", tomato: "#ff6347", turquoise: "#40e0d0", violet: "#ee82ee", wheat: "#f5deb3", white: "#ffffff", whitesmoke: "#f5f5f5", yellow: "#ffff00", yellowgreen: "#9acd32" }; export const CSS_COLOR_NAMES = Object.keys(CSS_COLORS); export const CSS_COLOR_NAME_REGEXP = new RegExp( `\\b(? td > a")).map(a => a.textContent).filter(content => content.charAt(0) !== "@"); Copy printed object */ import { CSS_DECLARATION_VALUE_MAP } from "./css-decl-name-constants"; // https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule const CSS_AT_RULE_NAMES = [ "media", "font-face", "keyframes", "namespace", "charset", // custom "mixin", "content", "include" ].sort(); const CSS_DECLARATION_NAMES = Object.keys(CSS_DECLARATION_VALUE_MAP); // https://www.w3schools.com/cssref/pr_text_white-space.asp const WHITE_SPACE_VALUES = [ "normal", "nowrap", "pre", "pre-line", "pre-wrap", "initial", "inherit" ]; // https://www.w3schools.com/cssref/css3_pr_flex-direction.asp const FLEX_DIRECTION_VALUES = [ "row", "row-reverse", "column", "column-reverse", "initial", "inherit" ]; // https://www.w3schools.com/cssref/css3_pr_text-overflow.asp const TEXT_OVERFLOW_VALUES = [ "clip", "ellipsis", "string", "initial", "inherit" ]; // https://www.w3schools.com/cssref/pr_pos_vertical-align.asp const VERTICAL_ALIGN_VALUES = [ "baseline", "length", "sub", "super", "top", "text-bottom", "middle", "bottom", "text-bottom", "initial", "inherit" ]; // https://www.w3schools.com/css/css_overflow.asp const OVERFLOW_VALUES = ["visible", "hidden", "scroll", "auto"]; // https://www.w3schools.com/cssref/css3_pr_animation-name.asp const ANIMATION_NAME_VALUES = ["none", "initial", "inhert"]; // https://www.w3schools.com/cssref/css3_pr_animation-timing-function.asp // TODO - cub-bezie, steps const ANIMATION_TIMING_FUNCTION = "plinear|ease|ease-in|ease-out|ease-in-out|step-start|step-end|initial|inherit".split( "|" ); // https://www.w3schools.com/cssref/pr_background-repeat.asp const BACKGROUND_REPEAT_VALUES = [ "repeat", "repeat-x", "repeat-y", "no-repeat", "initial", "inherit" ]; // https://www.w3schools.com/cssref/css3_pr_animation-iteration-count.asp const ANIMATION_ITERATION_COUNT = "infinite|initial|inherit".split("|"); const ANIMATION_DIRECTION = "normal|reverse|alternate|alternate-reverse|initial|inherit".split( "|" ); export { CSS_AT_RULE_NAMES, CSS_DECLARATION_NAMES, CSS_DECLARATION_VALUE_MAP }; ================================================ FILE: packages/paperclip-language-service/src/error-service.ts ================================================ import { ChangedSheetsEvent, DiagnosticKind, DiffedEvent, EngineDelegateEvent, EngineDelegateEventKind, EngineErrorEvent, EngineErrorKind, EvaluatedEvent, GraphErrorEvent, LoadedEvent, RuntimeErrorEvent, StringRange, } from "@paperclip-ui/utils"; import { EngineDelegate } from "@paperclip-ui/core"; import { EventEmitter } from "events"; import { createListener } from "@paperclip-ui/common"; /** * The diagnostic's severity. */ export enum DiagnosticSeverity { Error = 1, Warning = 2, Information = 3, Hint = 4, } /** * Represents a diagnostic, such as a compiler error or warning. Diagnostic objects * are only valid in the scope of a resource. */ export interface Diagnostic { /** * The range at which the message applies */ range: StringRange; /** * The diagnostic's severity. Can be omitted. If omitted it is up to the * client to interpret diagnostics as error, warning, info or hint. */ severity?: DiagnosticSeverity; /** * The diagnostic's code, which usually appear in the user interface. */ code?: number | string; /** * A human-readable string describing the source of this * diagnostic, e.g. 'typescript' or 'super lint'. It usually * appears in the user interface. */ source?: string; /** * The diagnostic's message. It usually appears in the user interface */ message: string; } export type LintInfo = { uri: string; content: string; diagnostics: Diagnostic[]; }; export class DiagnosticService { private _em: EventEmitter; constructor(private _engine: EngineDelegate) { this._em = new EventEmitter(); _engine.onEvent(this._onEngineDelegateEvent); } onLinted(listener: (info: LintInfo) => void) { return createListener(this._em, "linted", listener); } private _onEngineDelegateEvent = (event: EngineDelegateEvent) => { switch (event.kind) { case EngineDelegateEventKind.Error: { this._onEngineErrorEvent(event); break; } case EngineDelegateEventKind.Loaded: case EngineDelegateEventKind.Diffed: case EngineDelegateEventKind.ChangedSheets: case EngineDelegateEventKind.Evaluated: { this._onEngineEvaluatedEvent(event); break; } } }; private _onEngineEvaluatedEvent( event: DiffedEvent | EvaluatedEvent | LoadedEvent | ChangedSheetsEvent ) { this._dispatchDiagnostics(event.uri); } private _onEngineErrorEvent(event: EngineErrorEvent) { try { switch (event.errorKind) { case EngineErrorKind.Graph: { return this._handleGraphError(event); } case EngineErrorKind.Runtime: { return this._handleRuntimeError(event); } } } catch (e) { console.error(e.stack); } } private _handleGraphError({ uri }: GraphErrorEvent) { this._dispatchDiagnostics(uri); } private _handleRuntimeError({ uri }: RuntimeErrorEvent) { this._dispatchDiagnostics(uri); } private _dispatchDiagnostics(uri: string) { // break out of recursion setImmediate(() => { const content = this._engine.getVirtualContent(uri); // need to provide content here too since listener may not have direct access // to state related to the linted diagnostics. this._em.emit("linted", { uri, content, diagnostics: this._lint(uri) }); }); } private _lint(uri: string): Diagnostic[] { return this._engine .lint(uri) .map((diag) => { switch (diag.diagnosticKind) { case DiagnosticKind.EngineError: { if (diag.errorKind === EngineErrorKind.Graph) { return createDiagnostic( DiagnosticSeverity.Error, diag.info.message, diag.info.range ); } else if (diag.errorKind === EngineErrorKind.Runtime) { return createDiagnostic( DiagnosticSeverity.Error, diag.message, diag.range ); } return null; } case DiagnosticKind.LintWarning: { return createDiagnostic( DiagnosticSeverity.Warning, diag.message, diag.source.textSource!.range ); } } }) .filter(Boolean); } } const createDiagnostic = ( severity: DiagnosticSeverity, message: string, range: StringRange ) => { return { severity, range, message: `${message}`, source: "ex", }; }; ================================================ FILE: packages/paperclip-language-service/src/index.ts ================================================ export * from "./language-service"; export * from "./state"; ================================================ FILE: packages/paperclip-language-service/src/language-service.ts ================================================ /* Considerations: - auto-suggest based on import + relative files - linting - emitting things to VS Code - changes coming from language server - Running language server in browser */ import { Observable } from "@paperclip-ui/common"; import { AutocompleteService } from "./autocomplete"; import { getAllAvailableNodes } from "./state"; import { collectASTInfo, ColorInfo } from "./collect-ast-info"; import { DiagnosticService, LintInfo } from "./error-service"; import { EngineDelegate, FileSystem, getEngineImports, } from "@paperclip-ui/core"; import { GetAllAvailableNodesOptions } from "."; export class PaperclipLanguageService { private _autocomplete: AutocompleteService; private _diagnostics: DiagnosticService; readonly events: Observable; constructor(private _engine: EngineDelegate, fs?: FileSystem) { this._autocomplete = new AutocompleteService(fs); this._diagnostics = new DiagnosticService(_engine); } /** */ onLinted(listener: (info: LintInfo) => void) { return this._diagnostics.onLinted(listener); } /** * Returns all definitions (meta + click functionality) */ getDefinitions(uri: string) { return this._collectASTInfo(uri).definitions; } /** * Used when imports are added */ getUniqueDocumentNamespace(uri: string) {} /** * returns all document links in the file */ getLinks(uri: string) { return this._collectASTInfo(uri).links; } /** */ getDocumentColors(uri: string): ColorInfo[] { return this._collectASTInfo(uri).colors; } /** * returns all available nodes in the project (text, native elements, custom components) */ getAllAvailableNodes(options: GetAllAvailableNodesOptions) { return getAllAvailableNodes(options, this._engine); } /** * Returns list of options fro autocomplete */ getAutoCompletionSuggestions(uri: string, position: number = Infinity) { return this._autocomplete.getSuggestions( uri, this._engine.getVirtualContent(uri).substring(0, position), this._engine.getLoadedData(uri), getEngineImports(uri, this._engine) ); } private _collectASTInfo(uri: string) { if (!this._engine.getLoadedData(uri)) { this._engine.open(uri); } return collectASTInfo( uri, this._engine.getLoadedGraph(), this._engine.getAllLoadedData() ); } } ================================================ FILE: packages/paperclip-language-service/src/state.ts ================================================ import { EngineDelegate, getEngineImports } from "@paperclip-ui/core"; import { Observable } from "@paperclip-ui/common"; import { DependencyNodeContent, getAttributeStringValue, getParts as getComponents, hasAttribute, isPaperclipFile, LoadedPCData, } from "@paperclip-ui/utils"; import { AutocompleteService } from "./autocomplete"; import { collectASTInfo, ColorInfo } from "./collect-ast-info"; import { DiagnosticService, LintInfo } from "./error-service"; import { ALL_TAG_NAMES } from "./tag-name-constants"; export enum AvailableNodeKind { Text = "Text", Element = "Element", Instance = "Instance", } export type AvailableBaseNode = { kind: TKind; displayName: string; name: string; description: string; }; export type AvailableText = AvailableBaseNode; export type AvailableElement = AvailableBaseNode; export type AvailableInstance = { // where does the component live?? sourceUri: string; // when active document is included and import already exists namespace?: string; } & AvailableBaseNode; export type AvailableNode = | AvailableText | AvailableElement | AvailableInstance; const AVILABLE_NATIVE_NODES: AvailableNode[] = [ { kind: AvailableNodeKind.Text, name: "text", displayName: "Text", description: "Native text", }, ...ALL_TAG_NAMES.map((tagName) => { return { kind: AvailableNodeKind.Element, displayName: tagName, name: tagName, description: "", } as AvailableElement; }), ]; export type GetAllAvailableNodesOptions = { activeUri?: string; }; export const getAllAvailableNodes = ( options: GetAllAvailableNodesOptions, engine: EngineDelegate ) => { const allData = engine.getAllLoadedData(); const instances: AvailableInstance[] = []; for (const uri in allData) { const ast = engine.getLoadedAst(uri) as DependencyNodeContent; if (!isPaperclipFile(uri)) { continue; } const exportedComponents = getComponents(ast).filter( (component) => hasAttribute("export", component) || options.activeUri === uri ); instances.push( ...exportedComponents.map((component) => { const displayName = getAttributeStringValue("as", component) as string; return { kind: AvailableNodeKind.Instance, displayName, name: displayName, description: "", sourceUri: uri, } as AvailableInstance; }) ); } return [...instances, ...AVILABLE_NATIVE_NODES]; }; ================================================ FILE: packages/paperclip-language-service/src/tag-name-constants.ts ================================================ import { merge } from "lodash"; const NATIVE_VISIBLE_TAGS = [ "a", "b", "body", "button", "canvas", "nav", "img", "i", "hr", "h1", "h2", "h3", "h4", "h5", "h6", "footer", "form", "div", "iframe", "input", "ul", "tbody", "td", "span", "p", "ol", "li" ]; const NATIVE_INVISIBLE_TAGS = []; export const NATIVE_TAG_NAMES = [ ...NATIVE_VISIBLE_TAGS, ...NATIVE_INVISIBLE_TAGS, "abbr", "acronym", "address", "applet", "area", "article", "aside", "audio", "base", "basefont", "bdi", "bdo", "bgsound", "big", "blink", "blockquote", "br", "caption", "center", "cite", "code", "col", "colgroup", "command", "content", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "dir", "dl", "dt", "element", "em", "embed", "fieldset", "figcaption", "figure", "font", "frame", "frameset", "head", "header", "hgroup", "html", "image", "ins", "isindex", "kbd", "keygen", "label", "legend", "link", "listing", "main", "map", "mark", "marquee", "math", "menu", "menuitem", "meta", "meter", "multicol", "nextid", "nobr", "noembed", "noframes", "noscript", "object", "optgroup", "option", "output", "param", "picture", "plaintext", "pre", "progress", "q", "rb", "rbc", "rp", "rt", "rtc", "ruby", "s", "samp", "script", "section", "select", "shadow", "slot", "small", "source", "spacer", "strike", "strong", "style", "sub", "summary", "sup", "svg", "table", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "var", "video", "wbr", "xmp" ].sort(); const RESERVED_TAG_NAMES = ["import", "fragment"]; const ALL_TAG_NAMES = [...NATIVE_TAG_NAMES, ...RESERVED_TAG_NAMES].sort(); // https://www.w3.org/TR/html401/struct/global.html#h-7.5.4 const VISIBLE_ELEMENT_ATTRIBUTE_NAMES = [ "id", "style", "class", "onClick", "onDoubleClick", "onMouseDown", "onMouseUp", "onMouseOver", "onMouseMove", "onMouseOut", "onKeyPress", "onKeyDown", "onKeyUp", // custom "component", "as" ]; let ELEMENT_ATTRIBUTES: Record = ALL_TAG_NAMES.reduce( (obj, tagName) => { obj[tagName] = ["component"]; return obj; }, {} ); ELEMENT_ATTRIBUTES = merge({}, ELEMENT_ATTRIBUTES, { import: ["src", "as"] }); for (const tagName of NATIVE_TAG_NAMES) { if (NATIVE_VISIBLE_TAGS.includes(tagName)) { ELEMENT_ATTRIBUTES[tagName] = [ ...ELEMENT_ATTRIBUTES[tagName], ...VISIBLE_ELEMENT_ATTRIBUTE_NAMES ]; } } export { ELEMENT_ATTRIBUTES, ALL_TAG_NAMES }; ================================================ FILE: packages/paperclip-language-service/src/test/basic-test.ts ================================================ import { expect } from "chai"; import { createMockEngine } from "@paperclip-ui/core/lib/test/utils"; import { PaperclipLanguageService } from ".."; import { AvailableNodeKind } from "../state"; describe(__filename + "#", () => { it(`Can return all available elements in a project`, () => { const engine = createMockEngine({ "/entry.pc": `
    `, }); engine.open("/entry.pc"); const service = new PaperclipLanguageService(engine); const nodes = service .getAllAvailableNodes({ activeUri: undefined }) .filter((node) => node.kind === AvailableNodeKind.Instance); expect(nodes).to.eql([ { kind: AvailableNodeKind.Instance, displayName: "Test", name: "Test", sourceUri: "/entry.pc", description: "", }, ]); }); it(`Returns private instances of document if URI is active`, () => { const engine = createMockEngine({ "/entry.pc": `
    `, }); engine.open("/entry.pc"); const service = new PaperclipLanguageService(engine); const nodes = service .getAllAvailableNodes({ activeUri: "/entry.pc" }) .filter((node) => node.kind === AvailableNodeKind.Instance); expect(nodes).to.eql([ { kind: AvailableNodeKind.Instance, displayName: "Test", name: "Test", sourceUri: "/entry.pc", description: "", }, { kind: AvailableNodeKind.Instance, displayName: "Test2", name: "Test2", sourceUri: "/entry.pc", description: "", }, ]); }); }); ================================================ FILE: packages/paperclip-language-service/src/test/colors-test.ts ================================================ import { expect } from "chai"; import { collectASTInfo } from "../collect-ast-info"; import { createMockEngineDelegate } from "@paperclip-ui/test-utils"; import { createEngineDelegate } from "@paperclip-ui/core"; describe(__filename + "#", () => { [ [ `it can pull basic color information from a style rule`, { "/entry.pc": ` `, }, [ { value: { red: 1, green: 0, blue: 0, alpha: 1, }, start: 58, end: 61, }, ], ], [ `it can pull color information out fo rgba`, { "/entry.pc": ` `, }, [ { value: { red: 0, green: 0, blue: 0, alpha: 1, }, start: 58, end: 74, }, ], ], [ `it can pull color information out of nested element`, { "/entry.pc": ` `, }, [ { value: { red: 0, green: 0, blue: 1, alpha: 1, }, start: 80, end: 84, }, ], ], [ `can pull color information out of media queries`, { "/entry.pc": ` `, }, [ { value: { red: 0, green: 0, blue: 1, alpha: 1, }, start: 91, end: 95, }, ], ], [ `can pull color information out of key frames`, { "/entry.pc": ` `, }, [ { value: { red: 1, green: 0, blue: 0, alpha: 1, }, start: 90, end: 93, }, ], ], [ `can pull color information out of scoped styles`, { "/entry.pc": `
    `, }, [ { value: { red: 0, green: 0.39215686274509803, blue: 0.19607843137254902, alpha: 1, }, start: 58, end: 75, }, ], ], [ `can pull color information out of variables`, { "/entry.pc": `
    `, }, [ { value: { red: 1, green: 0, blue: 0, alpha: 1, }, start: 56, end: 59, }, { value: { red: 1, green: 0, blue: 0, alpha: 1, }, start: 82, end: 90, }, ], ], [ `can pull color information out variables from other documents`, { "/entry.pc": `
    `, "/atoms.css": ` :root { --color: red; } `, }, [ { value: { red: 1, green: 0, blue: 0, alpha: 1, }, start: 96, end: 108, }, ], ], [ `Can return document colors if an import src is invalid`, { "/entry.pc": `
    `, "/atoms.css": ` :root { --color: red; } `, }, [ { value: { red: 1, green: 0, blue: 0, alpha: 1, }, start: 96, end: 108, }, ], ], ].forEach(([name, graph, expectedColors]: any) => { it(name, () => { const engine = createMockEngineDelegate(createEngineDelegate)(graph); engine.open("/entry.pc"); const info = collectASTInfo( "/entry.pc", engine.getLoadedGraph(), engine.getAllLoadedData() ); expect(info.colors).to.eql(expectedColors); }); }); it(`Can collect colors even if src is incorrect`, () => { const engine = createMockEngineDelegate(createEngineDelegate)({ "/entry.pc": `
    `, }); engine.open("/entry.pc"); engine.updateVirtualFileContent( "/entry.pc", `` ); const info = collectASTInfo( "/entry.pc", engine.getLoadedGraph(), engine.getAllLoadedData() ); expect(info.colors).to.eql([ { value: { red: 1, green: 0, blue: 0, alpha: 1, }, start: 31, end: 34, }, ]); }); }); ================================================ FILE: packages/paperclip-language-service/src/test/definitions-test.ts ================================================ import { expect } from "chai"; import { collectASTInfo } from "../collect-ast-info"; import { createMockEngineDelegate } from "@paperclip-ui/test-utils"; import { createEngineDelegate } from "@paperclip-ui/core"; describe(__filename + "#", () => { [ [ `Returns definition info about component instance`, { "/entry.pc": `
    ` }, [ { sourceUri: "/entry.pc", sourceRange: { start: { pos: 11, line: 2, column: 11 }, end: { pos: 38, line: 2, column: 38 } }, sourceDefinitionRange: { start: { pos: 11, line: 2, column: 11 }, end: { pos: 38, line: 2, column: 38 } }, instanceRange: { start: { pos: 50, line: 3, column: 12 }, end: { pos: 54, line: 3, column: 16 } } } ] ], [ `Returns definition info about component that's also an instance`, { "/entry.pc": `
    ` }, [ { sourceUri: "/entry.pc", sourceRange: { start: { pos: 11, line: 2, column: 11 }, end: { pos: 38, line: 2, column: 38 } }, sourceDefinitionRange: { start: { pos: 11, line: 2, column: 11 }, end: { pos: 38, line: 2, column: 38 } }, instanceRange: { start: { pos: 50, line: 3, column: 12 }, end: { pos: 54, line: 3, column: 16 } } }, { sourceUri: "/entry.pc", sourceRange: { start: { pos: 49, line: 3, column: 11 }, end: { pos: 78, line: 3, column: 40 } }, sourceDefinitionRange: { start: { pos: 49, line: 3, column: 11 }, end: { pos: 78, line: 3, column: 40 } }, instanceRange: { start: { pos: 90, line: 4, column: 12 }, end: { pos: 95, line: 4, column: 17 } } } ] ], [ `Returns definition info about imported component`, { "/entry.pc": ` `, "/module.pc": `
    ` }, [ { sourceUri: "/module.pc", sourceRange: { start: { pos: 11, line: 2, column: 11 }, end: { pos: 45, line: 2, column: 45 } }, sourceDefinitionRange: { start: { pos: 11, line: 2, column: 11 }, end: { pos: 45, line: 2, column: 45 } }, instanceRange: { start: { pos: 60, line: 3, column: 12 }, end: { pos: 69, line: 3, column: 21 } } } ] ], [ `Returns definition info about default imported component`, { "/entry.pc": ` `, "/module.pc": `
    ` }, [ { sourceUri: "/module.pc", sourceRange: { start: { pos: 11, line: 2, column: 11 }, end: { pos: 48, line: 2, column: 48 } }, sourceDefinitionRange: { start: { pos: 11, line: 2, column: 11 }, end: { pos: 48, line: 2, column: 48 } }, instanceRange: { start: { pos: 60, line: 3, column: 12 }, end: { pos: 64, line: 3, column: 16 } } } ] ], [ `Returns definition about instance defined in slot`, { "/entry.pc": `
    {} `, "/module.pc": `
    ` }, [ { sourceUri: "/entry.pc", sourceRange: { start: { pos: 11, line: 2, column: 11 }, end: { pos: 38, line: 2, column: 38 } }, sourceDefinitionRange: { start: { pos: 11, line: 2, column: 11 }, end: { pos: 38, line: 2, column: 38 } }, instanceRange: { start: { pos: 51, line: 3, column: 13 }, end: { pos: 55, line: 3, column: 17 } } } ] ] ].forEach(([name, graph, expectedLinks]: any) => { it(name, () => { const engine = createMockEngineDelegate(createEngineDelegate)(graph); engine.open("/entry.pc"); const info = collectASTInfo( "/entry.pc", engine.getLoadedGraph(), engine.getAllLoadedData() ); expect(info.definitions).to.eql(expectedLinks); }); }); }); ================================================ FILE: packages/paperclip-language-service/src/test/links-test.ts ================================================ import { expect } from "chai"; import { collectASTInfo } from "../collect-ast-info"; import { createMockEngineDelegate } from "@paperclip-ui/test-utils"; import { createEngineDelegate } from "@paperclip-ui/core"; describe(__filename + "#", () => { [ [ `returns link to import`, { "/entry.pc": ` `, "/test.pc": ` something ` }, [ { uri: "/test.pc", range: { start: { pos: 24, line: 2, column: 24 }, end: { pos: 33, line: 2, column: 33 } } } ] ] ].forEach(([name, graph, expectedLinks]: any) => { it(name, () => { const engine = createMockEngineDelegate(createEngineDelegate)(graph); engine.open("/entry.pc"); const info = collectASTInfo( "/entry.pc", engine.getLoadedGraph(), engine.getAllLoadedData() ); expect(info.links).to.eql(expectedLinks); }); }); }); ================================================ FILE: packages/paperclip-language-service/src/test/suggestion-test.ts ================================================ import { expect } from "chai"; import { createMockEngineDelegate, mockFs } from "@paperclip-ui/test-utils"; import { PaperclipLanguageService } from "../language-service"; import { ATTRIBUTE_NAME_COMPLETION_ITEMS, CSS_DECLARATION_NAME_COMPLETION_ITEMS, TAG_NAME_COMPLETION_ITEMS, } from "../completion-items"; import { addCompletionItemData } from "../utils"; import { createEngineDelegate } from "@paperclip-ui/core"; import { saveTmpFixtureFiles } from "@paperclip-ui/common/lib/test-utils"; describe(__filename + "#", () => { [ [ `Can create a suggestion for an element`, { "file:///entry.pc": ` addCompletionItemData(item, "file:///entry.pc") ), ], [ `Can create a suggestion for an attribute name`, { "file:///entry.pc": `
    addCompletionItemData(item, "file:///entry.pc") ), ], [ `Can create a suggestion for an attribute name`, { "file:///entry.pc": `
    addCompletionItemData(item, "file:///entry.pc") ), ], [ `Can provide autocomplete for decl name`, { "file:///entry.pc": ` {children}
    ` }, entry: `file:///entry.pc` }, document.getElementById("app") ).init(); ================================================ FILE: packages/paperclip-repl/src/index.html ================================================ Basic Paperclip example with React
    ================================================ FILE: packages/paperclip-repl/src/index.ts ================================================ ================================================ FILE: packages/paperclip-repl/tsconfig.json ================================================ { "compilerOptions": { "jsx": "react", "outDir": "lib", "target": "es5", "moduleResolution": "node", "module": "esnext", "declaration": true, "allowSyntheticDefaultImports": true, "esModuleInterop": true }, "exclude": ["node_modules", "*-test", "lib", "index.d.ts", "test"], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-repl/webpack/webpack.config-base.js ================================================ const path = require("path"); const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const BASE_DIR = path.resolve(__dirname, ".."); exports.BASE_DIR = BASE_DIR; exports.config = { mode: "development", experiments: { asyncWebAssembly: true, }, plugins: [ new MiniCssExtractPlugin(), new HtmlWebpackPlugin({ publicPath: "/", title: "React Demo", template: path.resolve(BASE_DIR, "src", "index.html"), }), new webpack.ProvidePlugin({ process: "process/browser.js", Buffer: ["buffer", "Buffer"], }), new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify( process.env.NODE_ENV || "development" ), }), ], resolve: { extensions: [".tsx", ".ts", ".js"], alias: { os: "os-browserify/browser", crypto: "crypto-browserify", constants: "constants-browserify", }, }, externals: { chokidar: "{}", "get-port": "{}", http: "{}", ws: "{}", child_process: "{}", execa: "{}", assert: "{}", globby: "{}", express: "{}", stream: "{}", "fs-extra": "{}", fs: "{}", }, module: { rules: [ { test: /\.(ts|tsx)?$/, loader: "ts-loader", include: [path.resolve(BASE_DIR, "src"), path.resolve("..")], exclude: [], }, { test: /\.pc$/, loader: "paperclip-loader", include: [path.resolve(BASE_DIR, "src"), path.resolve("..")], options: { config: require("../paperclip.config.json"), }, }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"], }, { test: /\.(png|jpe?g|gif|ttf|svg)$/i, use: [ { loader: "file-loader", }, ], }, ], }, }; ================================================ FILE: packages/paperclip-repl/webpack/webpack.config-esm.js ================================================ const path = require("path"); const { config, BASE_DIR } = require("./webpack.config-base"); module.exports = { ...config, entry: "./src/app.ts", output: { filename: "main.js", libraryTarget: "umd", library: "lib", path: path.resolve(BASE_DIR, "esm"), publicPath: "/" } }; ================================================ FILE: packages/paperclip-repl/webpack/webpack.config-web.js ================================================ const path = require("path"); const { config, BASE_DIR } = require("./webpack.config-base"); module.exports = { ...config, entry: "./src/entry.tsx", output: { filename: "[name]-[contenthash].js", path: path.resolve(BASE_DIR, "dist"), publicPath: "/" } }; ================================================ FILE: packages/paperclip-repl/webpack/webpack.config.js ================================================ module.exports = [ require("./webpack.config-esm"), require("./webpack.config-web") ]; ================================================ FILE: packages/paperclip-source-writer/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/paperclip-source-writer/README.md ================================================ ```typescript const writer = new PCSourceWriter(engine); writer.handleEvent({ kind: "ANNOTATIONS_CHANGED" }); writer.onContentChanged(function(uri, content) { vscode.updateContent(uri, content); }); ``` ================================================ FILE: packages/paperclip-source-writer/index.d.ts ================================================ export * from "./lib"; ================================================ FILE: packages/paperclip-source-writer/package.json ================================================ { "name": "@paperclip-ui/source-writer", "version": "18.5.6", "description": "Internal util for writing source code from the designer", "main": "lib/index.js", "publishConfig": { "access": "public" }, "scripts": { "build": "tsc", "build:watch": "tsc --watch --preserveWatchOutput" }, "author": "", "license": "ISC", "dependencies": { "@paperclip-ui/core": "^18.5.6", "@paperclip-ui/utils": "^18.5.6" }, "devDependencies": { "chai": "^4.2.0", "mocha": "^8.2.1", "typescript": "^4.2.3" }, "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a" } ================================================ FILE: packages/paperclip-source-writer/src/index.ts ================================================ export * from "./writer"; export * from "./mutations"; ================================================ FILE: packages/paperclip-source-writer/src/mutations.ts ================================================ export enum PCMutationActionKind { ANNOTATIONS_CHANGED = "ANNOTATIONS_CHANGED", EXPRESSION_DELETED = "EXPRESSION_DELETED", CSS_DECLARATION_CHANGED = "CSS_DECLARATION_CHANGED" } type BaseAction = { kind: TKind; }; export type AnnotationsChanged = { annotations?: Record; } & BaseAction; export type CSSDeclarationChanged = { name: string; value: string; } & BaseAction; export type ExpressionDeleted = {} & BaseAction< PCMutationActionKind.EXPRESSION_DELETED >; export type PCMutationAction = | AnnotationsChanged | ExpressionDeleted | CSSDeclarationChanged; export type PCMutation = { targetId: string; action: PCMutationAction; }; ================================================ FILE: packages/paperclip-source-writer/src/string-editor.ts ================================================ import { ContentChange } from "./writer"; export const editString = (input: string, changes: ContentChange[]) => { let output = input; for (let i = 0, n = changes.length; i < n; i++) { const { start, end, value } = changes[i]; let offsetStartIndex = start; let offsetEndIndex = end; let invalid = false; const insertion = start === end; // based on all of the previous edits, calculate where this edit is for (let j = 0; j < i; j++) { const { start: previousStartIndex, end: previousEndIndex, value: previousNewValue } = changes[j]; const prevInsertion = previousStartIndex === previousEndIndex; const startIndicesMatch = start === previousStartIndex; const endIndicesMatch = end === previousEndIndex; // input : a b c d e f g h i // prev : ^-------^ // ✔ : ^ const insertBeginning = startIndicesMatch && insertion; // input : a b c d e f g h i // prev : ^-------^ // ✔ : ^ const insertEnd = endIndicesMatch && insertion; // input : a b c d e f g h i // prev : ^ // ✔ : ^-------^ const prevInsertBeginning = startIndicesMatch && prevInsertion; // input : a b c d e f g h i // prev : ^ // ✔ : ^-------^ const prevInsertEnd = endIndicesMatch && prevInsertion; const currOrPrevInserting = insertBeginning || insertEnd || prevInsertBeginning || prevInsertEnd; // input : a b c d e f g h i // prev : ^-------^ // ✔ : ^-------^ if (previousStartIndex < end && previousStartIndex > start) { offsetEndIndex = offsetEndIndex - (end - previousStartIndex); } // input : a b c d e f g h i // prev : ^-----^ // ✔ : ^-------^ if (previousEndIndex > start && previousEndIndex < end) { offsetStartIndex = offsetStartIndex + (previousEndIndex - start); } // Invalid edit because previous replacement // completely clobbers this one. There's nothing else to edit. // input : a b c d e f g h i // prev : ^---------^ // ✔ : ^---^ // ✔ : ^-------------^ // ✘ : ^ // ✘ : ^ // ✘ : ^-----------^ if ( ((start >= previousStartIndex && end <= previousEndIndex) || (start < previousStartIndex && end >= previousEndIndex)) && !currOrPrevInserting ) { invalid = true; break; } // input : a b c d e f g h // prev : ^-----^ // ✔ : ^-----^ // ✔ : ^---^ // ✔ : ^-^ // ✔ : ^-----^ // ✘ : ^---^ // ✘ : ^-^ // ✘ : ^ // input : a b c d e f g h // prev : ^---^ // ✔ : ^---^ if (previousStartIndex <= start && end > previousStartIndex) { const prevValueLengthDelta = previousNewValue.length - (previousEndIndex - previousStartIndex); // shift left or right offsetStartIndex = Math.max(0, offsetStartIndex + prevValueLengthDelta); offsetEndIndex = Math.max(0, offsetEndIndex + prevValueLengthDelta); } } if (!invalid) { output = output.substr(0, offsetStartIndex) + value + output.substr(offsetEndIndex); } } return output; }; ================================================ FILE: packages/paperclip-source-writer/src/test/basic-test.ts ================================================ // import { expect } from "chai"; // import { PCMutationActionKind } from "../mutations"; // import { PCSourceWriter } from "../writer"; // import { createMockEngineDelegate } from "./utils"; // describe(__filename + "#", () => { // it(`Can update existing annotations`, async () => { // const graph = { // "/entry.pc": ` // // // ` // }; // const engine = await createMockEngineDelegate(graph); // engine.open("/entry.pc"); // const writer = new PCSourceWriter({ // getContent(uri) { // return graph[uri]; // } // }); // const changes = writer.getContentChanges({ // action: { // kind: PCMutationActionKind.ANNOTATIONS_CHANGED, // annotations: { // a: { // b: 10 // } // } // } // }); // console.log(changes); // }); // }); ================================================ FILE: packages/paperclip-source-writer/src/test/utils.ts ================================================ import { EngineMode, createEngineDelegate } from "@paperclip-ui/core"; import * as path from "path"; export const mockDOMFactory: any = { createElement: tagName => (new MockElement(tagName) as any) as HTMLElement, createElementNS: tagName => (new MockElement(tagName) as any) as HTMLElement, createDocumentFragment: () => (new MockFragment() as any) as DocumentFragment, createTextNode: nodeValue => (new MockTextNode(nodeValue) as any) as Text }; abstract class BaseNode { $$parent: ParentNode; remove() { this.$$parent.removeChild(this); } get parentNode() { return this.$$parent; } get innerHTML() { return this.getInnerHTML(); } abstract cloneNode(); abstract getInnerHTML(); abstract toString(); } abstract class ParentNode extends BaseNode { childNodes: BaseNode[] = []; appendChild(child: BaseNode) { child.$$parent = this; if (child instanceof MockFragment) { child.childNodes.forEach(child => { child.$$parent = this; }); this.childNodes.push(...child.childNodes); } else { this.childNodes.push(child); } } removeChild(child: BaseNode) { child.$$parent = null; this.childNodes.splice(this.childNodes.indexOf(child), 1); } insertBefore(child: BaseNode, ref: BaseNode) { const index = this.childNodes.indexOf(ref); if (index === -1) { throw new Error(`ref not found`); } const children = child instanceof MockFragment ? child.childNodes : [child]; for (const child of children) { child.$$parent = this; } this.childNodes.splice(index, 0, ...children); } getInnerHTML() { let buffer = ""; for (const child of this.childNodes) { buffer += child.toString(); } return buffer; } } class MockElement extends ParentNode { attributes = {}; style = {}; textContent = ""; constructor(readonly tagName: string) { super(); } setAttribute(name: string, value: string) { this.attributes[name] = value; } cloneNode() { const el = new MockElement(this.tagName); for (const key in this.attributes) { el.setAttribute(key, el.attributes[key]); } el.textContent = this.textContent; for (const child of this.childNodes) { el.appendChild(child.cloneNode()); } return el; } // eslint-disable-next-line addEventListener() {} removeAttribute(name: string) { delete this.attributes[name]; } toString() { let buffer = `<${this.tagName}`; const sortedAttributes = Object.keys(this.attributes) .sort() .map(name => ({ name, value: this.attributes[name] })); for (const { name, value } of sortedAttributes) { if (!value) { continue; } buffer += ` ${name}=${JSON.stringify(value)}`; } buffer += `>`; buffer += this.getInnerHTML() || this.textContent.replace(/[\s\r\n\t]+/g, " "); buffer += ``; return buffer; } } class MockFragment extends ParentNode { cloneNode() { const clone = new MockFragment(); for (const child of this.childNodes) { clone.appendChild(child.cloneNode()); } return clone; } toString() { return ""; } } class MockTextNode extends BaseNode { constructor(public nodeValue: string) { super(); } cloneNode() { return new MockTextNode(this.nodeValue); } getInnerHTML() { return this.toString(); } toString() { return this.nodeValue; } } export type Graph = { [identifier: string]: string; }; export const createMockEngine = (graph: Graph) => createEngineDelegate({ io: { readFile: uri => graph[uri.replace("file://", "")] || graph[uri.replace(/\\+/g, "/")], fileExists: uri => Boolean( graph[uri.replace("file://", "")] || graph[uri.replace(/\\+/g, "/")] ), resolveFile: (from, to) => { const prefix = from.indexOf("file:") === 0 ? "file://" : ""; return ( prefix + path .join(path.dirname(from.replace("file://", "")), to) .replace(/\\+/g, "/") ); } } }); export const createMockEngineDelegate = ( graph: Graph, mode: EngineMode = EngineMode.SingleFrame ) => createEngineDelegate({ io: { readFile: uri => graph[uri.replace("file://", "")] || graph[uri.replace(/\\+/g, "/")], fileExists: uri => Boolean( graph[uri.replace("file://", "")] || graph[uri.replace(/\\+/g, "/")] ), resolveFile: (from, to) => { const prefix = from.indexOf("file:") === 0 ? "file://" : ""; return ( prefix + path .join(path.dirname(from.replace("file://", "")), to) .replace(/\\+/g, "/") ); } }, mode }); export const trimWS = (str: string) => str.replace(/[\s\r\n\t]+/g, " "); ================================================ FILE: packages/paperclip-source-writer/src/writer.ts ================================================ import { EngineDelegate } from "@paperclip-ui/core"; import { CSSDeclarationChanged, PCMutation, PCMutationActionKind, } from "./mutations"; import { ExprTextSource, StringRange, NodeKind, Node, traverseExpression, Expression, getParentNode, DependencyNodeContent, getPCNodeAnnotations, getNodeById, } from "@paperclip-ui/utils"; import { editString } from "./string-editor"; export type ContentChange = { uri: string; start: number; end: number; value: string; }; const ANNOTATION_KEYS = ["title", "width", "height", "x", "y"]; // TODO: add more tests here export class PCSourceWriter { constructor(private _engine: EngineDelegate) {} apply(mutations: PCMutation[]): Record { const changes = this._getContentChanges(mutations); for (const uri in changes) { const newContent = editString( this._engine.getVirtualContent(uri), changes[uri] ); this._engine.updateVirtualFileContent(uri, newContent); } // return changes so that return changes; } private _getContentChanges( mutations: PCMutation[] ): Record { const changes: ContentChange[] = []; const engine = this._engine; for (const { targetId, action } of mutations) { const [uri, targetAst] = engine.getExpressionById(targetId); const documentAst = engine.getLoadedAst(uri) as DependencyNodeContent; const textSource = { uri, range: targetAst.range }; switch (action.kind) { case PCMutationActionKind.ANNOTATIONS_CHANGED: { changes.push( this._getAnnotationChange( textSource, getPCNodeAnnotations( getNodeById((targetAst as Node).id, documentAst), documentAst )?.range, action.annotations ) ); break; } case PCMutationActionKind.CSS_DECLARATION_CHANGED: { changes.push(this._getCSSDeclarationChange(textSource, action)); break; } case PCMutationActionKind.EXPRESSION_DELETED: { changes.push( ...this._getExpressionDeletedChanged(textSource, targetAst as Node) ); break; } } } const changesByUri = {}; for (const change of changes) { if (!changesByUri[change.uri]) { changesByUri[change.uri] = []; } changesByUri[change.uri].push(change); } return changesByUri; } private _getExpressionDeletedChanged( exprTextSource: ExprTextSource, ast: Node ): ContentChange[] { const node = getAssocNode(exprTextSource, ast); const parent = getParentNode(node, ast); const childIndex = parent.children.findIndex((child) => child === node); const changes = []; const beforeChild = childIndex > 0 ? parent.children[childIndex - 1] : null; // if before child is a comment, then assume it's an annotation if (beforeChild && beforeChild.nodeKind === NodeKind.Comment) { changes.push({ uri: exprTextSource.uri, start: beforeChild.range.start.pos, end: beforeChild.range.end.pos, value: "", }); } changes.push({ uri: exprTextSource.uri, start: exprTextSource.range.start.pos, end: exprTextSource.range.end.pos, value: "", }); return changes; } private _getCSSDeclarationChange( exprTextSource: ExprTextSource, action: CSSDeclarationChanged ) { return { uri: exprTextSource.uri, start: exprTextSource.range.start.pos, end: exprTextSource.range.end.pos, value: `${action.name}: ${action.value};`, }; } private _getAnnotationChange( exprTextSource: ExprTextSource, annotationsRange: StringRange | null, annotations: Object | null ): ContentChange { const buffer = [""); // insertion - give it some padding if (!annotationsRange) { buffer.unshift("\n"); buffer.push("\n"); } return { uri: exprTextSource.uri, start: annotationsRange ? annotationsRange.start.pos : exprTextSource.range.start.pos, end: annotationsRange ? annotationsRange.end.pos : exprTextSource.range.start.pos, // newline may have been clipped off, so re-add if that happens value: buffer.join(""), }; } } const getAssocNode = (exprTextSource: ExprTextSource, root: Node): Node => { let foundExpr: Expression; traverseExpression(root, null, (node) => { if ( node.range.start.pos === exprTextSource.range.start.pos && node.range.end.pos === exprTextSource.range.end.pos ) { foundExpr = node; return false; } }); // should NOT happen if (!foundExpr) { console.error( `[PCSourceWriter] Cannot find associated node, content is likely out of sync with visual editor.` ); } return foundExpr as Node; }; ================================================ FILE: packages/paperclip-source-writer/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./lib", "target": "es5", "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true, "declaration": true }, "exclude": [ "**/*.d.ts", "esm", "native", "lib", "node_modules", "*-test", "index.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-test-utils/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/paperclip-test-utils/index.d.ts ================================================ export * from "./src"; ================================================ FILE: packages/paperclip-test-utils/index.js ================================================ module.exports = require("./lib"); ================================================ FILE: packages/paperclip-test-utils/package.json ================================================ { "name": "@paperclip-ui/test-utils", "version": "18.5.2", "description": "", "main": "index.js", "publishConfig": { "access": "public" }, "scripts": { "build": "yarn tsc", "build:watch": "yarn tsc --watch --preserveWatchOutput" }, "author": "", "license": "ISC", "dependencies": { "cssom": "^0.5.0", "lodash": "^4.17.21" }, "devDependencies": { "typescript": "^4.2.3" }, "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a" } ================================================ FILE: packages/paperclip-test-utils/src/index.ts ================================================ import * as path from "path"; import * as CSSOM from "cssom"; import * as url from "url"; export const mockDOMFactory = { createElement: (tagName) => { if (tagName === "style") { return new StyleElement() as any as HTMLElement; } return new MockElement(tagName) as any as HTMLElement; }, createElementNS: (tagName) => new MockElement(tagName) as any as HTMLElement, createDocumentFragment: () => new MockFragment() as any as DocumentFragment, createTextNode: (nodeValue) => new MockTextNode(nodeValue) as any as Text, }; abstract class BaseNode { $$parent: ParentNode; remove() { this.$$parent.removeChild(this); } get parentNode() { return this.$$parent; } get innerHTML() { return this.getInnerHTML(); } abstract cloneNode(); abstract getInnerHTML(); abstract toString(); } abstract class ParentNode extends BaseNode { childNodes: BaseNode[] = []; appendChild(child: BaseNode) { child.$$parent = this; if (child instanceof MockFragment) { child.childNodes.forEach((child) => { child.$$parent = this; }); this.childNodes.push(...child.childNodes); } else { this.childNodes.push(child); } } removeChild(child: BaseNode) { child.$$parent = null; this.childNodes.splice(this.childNodes.indexOf(child), 1); } insertBefore(child: BaseNode, ref: BaseNode) { const index = this.childNodes.indexOf(ref); if (index === -1) { throw new Error(`ref not found`); } const children = child instanceof MockFragment ? child.childNodes : [child]; for (const child of children) { child.$$parent = this; } this.childNodes.splice(index, 0, ...children); } getInnerHTML() { let buffer = ""; for (const child of this.childNodes) { buffer += child.toString(); } return buffer; } } class StyleElement extends ParentNode { private _textContent: string; private _sheet: CSSOM.StyleSheet; get textContent() { return this._textContent; } set textContent(value: string) { this._textContent = value; this._sheet = CSSOM.parse(value); } get sheet() { return this._sheet; } cloneNode() { const el = new StyleElement(); el.textContent = this.sheet.toString(); return el; } toString() { return ``; } } class MockElement extends ParentNode { attributes = {}; style = {}; textContent = ""; constructor(readonly tagName: string) { super(); } setAttribute(name: string, value: string) { this.attributes[name] = value; } cloneNode() { const el = new MockElement(this.tagName); for (const key in this.attributes) { el.setAttribute(key, el.attributes[key]); } el.textContent = this.textContent; for (const child of this.childNodes) { el.appendChild(child.cloneNode()); } return el; } // eslint-disable-next-line addEventListener() {} removeAttribute(name: string) { delete this.attributes[name]; } toString() { let buffer = `<${this.tagName}`; const sortedAttributes = Object.keys(this.attributes) .sort() .map((name) => ({ name, value: this.attributes[name] })); for (const { name, value } of sortedAttributes) { if (!value) { continue; } buffer += ` ${name}=${JSON.stringify(value)}`; } buffer += `>`; buffer += this.getInnerHTML() || this.textContent.replace(/[\s\r\n\t]+/g, " "); buffer += ``; return buffer; } } class MockFragment extends ParentNode { cloneNode() { const clone = new MockFragment(); for (const child of this.childNodes) { clone.appendChild(child.cloneNode()); } return clone; } toString() { return ""; } } class MockTextNode extends BaseNode { constructor(public nodeValue: string) { super(); } cloneNode() { return new MockTextNode(this.nodeValue); } getInnerHTML() { return this.toString(); } toString() { return this.nodeValue; } } export type Graph = { [identifier: string]: string; }; const stripFileProtocol = (uri) => uri.replace("file://", ""); const mockReadFile = (graph: Graph) => (uri: string | URL) => { const path = uri instanceof URL ? uri.href : uri; return graph[path.replace("file://", "")] || graph[path.replace(/\\+/g, "/")]; }; const mockReadDir = (graph: Graph) => (uri: string | URL) => { const path = uri instanceof URL ? url.fileURLToPath(uri.href) : uri; return Object.keys(graph) .map(stripFileProtocol) .filter((fp) => fp.indexOf(path) === 0) .map((p) => p.split("/").pop()); }; const graphContains = (graph, path) => Object.keys(graph).some((fp) => stripFileProtocol(fp).indexOf(path) === 0); const mockLStatSync = (graph: Graph) => (uri: string | URL) => { const path = uri instanceof URL ? url.fileURLToPath(uri.href) : uri; // simulate lstat sync if (!graphContains(graph, path)) { throw new Error(`no such file or directory`); } return { isDirectory() { return ( !graph[path] && !graph["file://" + path] && graphContains(graph, path) ); }, isSymbolicLink() { return false; }, }; }; const mockExistsSync = (graph: Graph) => { return (uri) => { const path = uri instanceof URL ? url.fileURLToPath(uri.href) : uri; return graphContains(graph, path); }; }; export const mockFs = (graph: Graph) => ({ realpathSync(uri: string | URL) { if (uri instanceof URL) { return url.fileURLToPath(uri.href); } return uri; }, readFileSync: mockReadFile(graph), readdirSync: mockReadDir(graph), existsSync: mockExistsSync(graph), lstatSync: mockLStatSync(graph), }); export const createMockEngineDelegate = (engineFactory) => (graph: Graph, mode = "SingleFrame") => engineFactory({ io: { readFile: mockReadFile(graph), fileExists: (uri) => Boolean( graph[uri.replace("file://", "")] || graph[uri.replace(/\\+/g, "/")] ), resolveFile: (from, to) => { const prefix = from.indexOf("file:") === 0 ? "file://" : ""; return ( prefix + path .join(path.dirname(from.replace("file://", "")), to) .replace(/\\+/g, "/") ); }, }, mode, }); // eslint-disable-next-line const noop = () => {}; export const trimWS = (str: string) => str.replace(/[\s\r\n\t]+/g, " "); ================================================ FILE: packages/paperclip-test-utils/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./lib", "target": "es5", "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true, "strict": false /* Enable all strict type-checking options. */ }, "exclude": [ "**/*.d.ts", "lib/**/*.d.ts", "esm", "native", "lib/**/*", "node_modules", "*-test", "index.d.ts", "browser.d.ts", "test", "examples" ], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-utils/.eslintrc.json ================================================ { "root": true, "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], "rules": { "no-constant-condition": 0 } } ================================================ FILE: packages/paperclip-utils/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/paperclip-utils/README.md ================================================ ================================================ FILE: packages/paperclip-utils/index.d.ts ================================================ export * from "./lib"; ================================================ FILE: packages/paperclip-utils/index.js ================================================ module.exports = require("./lib"); ================================================ FILE: packages/paperclip-utils/package.json ================================================ { "name": "@paperclip-ui/utils", "version": "18.5.6", "description": "Internal utils for Paperclip", "main": "index.js", "publishConfig": { "access": "public" }, "scripts": { "build": "tsc", "lint": "eslint src", "build:watch": "tsc --watch --preserveWatchOutput", "test": "mocha ./lib/test/**/*-test.js" }, "author": "", "license": "MIT", "dependencies": { "@types/mocha": "^5.2.7", "chai": "^4.2.0", "crc32": "^0.2.2", "events": "^3.2.0", "html-entities": "^1.3.1", "lru-cache": "^6.0.0", "path": "^0.12.7", "url": "^0.11.0" }, "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a", "devDependencies": { "@types/chokidar": "^2.1.3", "@types/glob": "^7.1.3", "@types/node": "^14.0.20", "@typescript-eslint/eslint-plugin": "^3.6.1", "@typescript-eslint/parser": "^3.6.1", "chokidar": "^3.4.1", "eslint": "^7.4.0", "glob": "^7.1.6", "mocha": "^7.2.0", "typescript": "^4.2.3" } } ================================================ FILE: packages/paperclip-utils/src/base/ast.ts ================================================ export type StringPosition = { pos: number; line: number; column: number; }; export type StringRange = { start: StringPosition; end: StringPosition; }; export type BasicRaws = { before: string; after: string; }; ================================================ FILE: packages/paperclip-utils/src/base/virt.ts ================================================ import { StringRange } from "./ast"; export type ExprTextSource = { uri: string; range: StringRange; }; export type ExprSource = { sourceId: string; textSource?: ExprTextSource; }; ================================================ FILE: packages/paperclip-utils/src/core/actions.ts ================================================ import { Node } from "../html/ast"; import { EngineDelegateEvent } from "./events"; import { DependencyContent } from "./graph"; import { LoadedData } from "../html/virt"; export enum BasicPaperclipActionType { AST_REQUESTED = "AST_REQUESTED", LOADED_DATA_REQUESTED = "LOADED_DATA_REQUESTED", LOADED_DATA_EMITTED = "LOADED_DATA_EMITTED", AST_EMITTED = "AST_EMITTED", ENGINE_DELEGATE_CHANGED = "ENGINE_DELEGATE_CHANGED", PREVIEW_CONTENT = "PREVIEW_CONTENT" } export const actionCreator = >( type: TAction["type"] ) => (payload: TAction["payload"]) => ({ type, payload }); type BaseAction = { type: TType; payload: TPayload; }; export type AstRequested = BaseAction< BasicPaperclipActionType.AST_REQUESTED, { uri: string } >; export type LoadedDataRequested = BaseAction< BasicPaperclipActionType.LOADED_DATA_REQUESTED, { uri: string } >; export type LoadedDataEmitted = BaseAction< BasicPaperclipActionType.LOADED_DATA_EMITTED, { uri: string; data: LoadedData; imports: Record; ast: DependencyContent; } >; export type PreviewContent = BaseAction< BasicPaperclipActionType.PREVIEW_CONTENT, { uri: string; value: string } >; export type AstEmitted = BaseAction< BasicPaperclipActionType.AST_EMITTED, { uri: string; content: DependencyContent } >; export type EngineDelegateChanged = BaseAction< BasicPaperclipActionType.ENGINE_DELEGATE_CHANGED, EngineDelegateEvent >; export const astEmitted = actionCreator( BasicPaperclipActionType.AST_EMITTED ); export const astRequested = actionCreator( BasicPaperclipActionType.AST_REQUESTED ); export const loadedDataRequested = actionCreator( BasicPaperclipActionType.LOADED_DATA_REQUESTED ); export const loadedDataEmitted = actionCreator( BasicPaperclipActionType.LOADED_DATA_EMITTED ); export const previewContent = actionCreator( BasicPaperclipActionType.PREVIEW_CONTENT ); export const engineDelegateChanged = actionCreator( BasicPaperclipActionType.ENGINE_DELEGATE_CHANGED ); export type BasicPaperclipAction = | AstEmitted | AstRequested | LoadedDataRequested | LoadedDataEmitted | EngineDelegateChanged | PreviewContent; ================================================ FILE: packages/paperclip-utils/src/core/ast.ts ================================================ import { StringRange } from "../base/ast"; export type StringLiteral = { id: string; value: string; range: StringRange; }; ================================================ FILE: packages/paperclip-utils/src/core/config.ts ================================================ import * as path from "path"; import { paperclipSourceGlobPattern } from "./utils"; // TODO - ability to include vendor in extension export type CompilerOptions = { // give room for custom props [identifier: string]: any; target?: string; // [d.ts, js, ] emit?: string[]; // where PC files should be compiled to. If undefined, then // srcDir is used outDir: string; // treat assets as modules importAssetsAsModules?: boolean; // main CSS file name mainCSSFileName?: string; // embed assets until this size embedAssetMaxSize?: number; // output directory for non-PC files. If not specified, then srcDir // will be used assetOutDir?: string; // prefix for assets, assetPrefix?: string; useAssetHashNames?: boolean; }; export type CompilerOptionsTemplate = Omit & { outDir?: string; // base?: boolean; }; /* [ [ { base: true, target: "target" }, { outDir: "src" } ], [ { base: true, target: "html" } ] ] */ // export type CompilerOptionTemplates = CompilerOptionsTemplate | CompilerOptionsTemplate[] | CompilerOptionTemplates[]; export type CompilerOptionTemplates = | CompilerOptionsTemplate | CompilerOptionsTemplate[]; /** */ type LintOptions = { // flag CSS code that is not currently used noUnusedStyles?: boolean; // enforce CSS vars for these properties enforceVars?: string[]; }; export type PaperclipConfig = { // source directory where *.pc files live srcDir?: string; // directories where modules are stored moduleDirs?: string[]; // options for the output settings compilerOptions?: CompilerOptionTemplates; lintOptions?: LintOptions; // include?: string[]; }; export const getPaperclipConfigIncludes = ( config: PaperclipConfig, cwd: string ) => { if (config.srcDir) { // This should actually be **RESOURCE** global pattern including CSS files. However, // we can't do that now since there may be CSS files that clobber the PC engine. What we // need to do is load CSS resources based on what each PC file loads return [paperclipSourceGlobPattern(path.join(cwd, config.srcDir))]; } // if (config.include) { // return config.include.map(inc => path.join(cwd, inc)); // } return [path.join(paperclipSourceGlobPattern(cwd))]; }; /** */ export const buildCompilerOptions = ( config: PaperclipConfig ): CompilerOptions[] => { return buildCompilerOptionsFromTemplates(config, config.compilerOptions); }; /** */ const buildCompilerOptionsFromTemplates = ( config: PaperclipConfig, templates?: CompilerOptionTemplates ): CompilerOptions[] => { if (!templates) { return [{ outDir: config.srcDir }]; } if (!Array.isArray(templates)) { templates = Array.isArray(templates) ? templates : [templates]; } // const base = templates.find(template => !Array.isArray(template) && template.base) as CompilerOptionsTemplate; return templates.reduce((allCompilerOptions, template) => { const compilerOptions = template; allCompilerOptions.push({ outDir: config.srcDir, ...compilerOptions, }); return allCompilerOptions; }, []); }; /** */ export const getOutputFile = ( filePath: string, config: PaperclipConfig, compilerOptions: CompilerOptions | null, cwd: string ) => { return compilerOptions?.outDir ? filePath.replace( path.join(cwd, config.srcDir), path.join(cwd, compilerOptions.outDir) ) : filePath; }; ================================================ FILE: packages/paperclip-utils/src/core/constants.ts ================================================ export const PC_CONFIG_FILE_NAME = "paperclip.config.json"; export const DEFAULT_PART_ID = "default"; export const EXPORT_TAG_NAME = "export"; export const COMPONENT_ATTR_NAME = "component"; export const PREVIEW_ATTR_NAME = "preview"; export const FRAGMENT_TAG_NAME = "fragment"; export const AS_ATTR_NAME = "as"; export const INJECT_STYLES_TAG_NAME = "inject-styles"; export const PC_MODULE_NAMESPACE = "@paperclip-ui"; // deprecated export const LOGIC_TAG_NAME = "logic"; export const ELEMENT_INSERT_ATTR = "data-pc-show-insert"; ================================================ FILE: packages/paperclip-utils/src/core/diagnostics.ts ================================================ import { ExprSource, ExprTextSource } from "../base/virt"; import { EngineError } from "./errors"; export enum LintWarningKind {} export enum DiagnosticKind { EngineError = "EngineError", LintWarning = "LintWarning" } export type BaseDiagnostic = { diagnosticKind: TKind; }; export type DiagnosticEngineError = EngineError & BaseDiagnostic; export type DiagnosticLintWarning = { warningKind: LintWarningKind; message: string; source: ExprSource; } & BaseDiagnostic; export type Diagnostic = DiagnosticEngineError | DiagnosticLintWarning; ================================================ FILE: packages/paperclip-utils/src/core/errors.ts ================================================ import { StringRange } from "../base/ast"; export enum ParseErrorKind { EndOfFile = "EndOfFile", Unknown = "Unknown", Unexpected = "Unexpected", Unterminated = "Unterminated" } export type ParseError = { kind: ParseErrorKind; message: string; range: StringRange; }; export enum GraphErrorInfoKind { IncludeNotFound = "IncludeNotFound", Syntax = "Syntax" } export type BaseGraphErrorInfo = { kind: TKind; }; export type IncludeNotFoundErrorInfo = { uri: string; range: StringRange; message: string; } & BaseGraphErrorInfo; export type SyntaxErrorInfo = ParseError & BaseGraphErrorInfo; export type GraphErrorInfo = IncludeNotFoundErrorInfo | SyntaxErrorInfo; export type GraphError = { uri: string; info: GraphErrorInfo; }; export type RuntimeError = { uri: string; range: StringRange; message: string; }; export enum EngineErrorKind { Graph = "Graph", Runtime = "Runtime" } export type BaseEngineError = { errorKind: TKind; }; export type RuntimeEngineError = RuntimeError & BaseEngineError; export type GraphEngineError = GraphError & BaseEngineError; export type EngineError = RuntimeEngineError | GraphEngineError; ================================================ FILE: packages/paperclip-utils/src/core/events.ts ================================================ // TODO - move all non-specific event stuff to payload, or data prop so that // event can remain ephemeral. import { Node } from "../html/ast"; import { StringRange } from "../base/ast"; import { EngineErrorKind, GraphErrorInfo } from "./errors"; import { EvaluatedData, DiffedData, LoadedData, SheetInfo } from "../html/virt"; export enum EngineDelegateEventKind { Loading = "Loading", Deleted = "Deleted", Loaded = "Loaded", Updating = "Updating", Evaluated = "Evaluated", Error = "Error", NodeParsed = "NodeParsed", Diffed = "Diffed", ChangedSheets = "ChangedSheets" } type BaseEngineDelegateEvent = { kind: KKind; }; export type EvaluatedEvent = { uri: string; data: EvaluatedData; } & BaseEngineDelegateEvent; export type DeletedEvent = { uri: string; } & BaseEngineDelegateEvent; export type DiffedEvent = { uri: string; data: DiffedData; } & BaseEngineDelegateEvent; export type NodeParsedEvent = { uri: string; node?: Node; } & BaseEngineDelegateEvent; export type ChangedSheetsData = { newSheets: SheetInfo[]; removedSheetUris: string[]; allImportedSheetUris: string[]; }; export type ChangedSheetsEvent = { uri: string; data: ChangedSheetsData; } & BaseEngineDelegateEvent; export type BaseEngineErrorEvent = { uri: string; errorKind: TErrorType; } & BaseEngineDelegateEvent; export type GraphErrorEvent = { info: GraphErrorInfo; message: string; } & BaseEngineErrorEvent; export type RuntimeErrorEvent = { uri: string; message: string; range: StringRange; } & BaseEngineErrorEvent; export type LoadedEvent = { uri: string; data: LoadedData; } & BaseEngineDelegateEvent; export type EngineErrorEvent = GraphErrorEvent | RuntimeErrorEvent; export type EngineDelegateEvent = | EvaluatedEvent | EngineErrorEvent | ChangedSheetsEvent | DeletedEvent | NodeParsedEvent | LoadedEvent | DiffedEvent; ================================================ FILE: packages/paperclip-utils/src/core/graph.ts ================================================ import { Node } from "../html/ast"; import { Sheet } from "../css/ast"; export enum DependencyContentKind { Node = "Node", Stylsheet = "Stylesheet" } export type BaeDependencyContent = { contentKind: TKind; }; export type DependencyNodeContent = BaeDependencyContent< DependencyContentKind.Node > & Node; export type DependencyStyleSheetContent = BaeDependencyContent< DependencyContentKind.Stylsheet > & Sheet; export type DependencyContent = | DependencyNodeContent | DependencyStyleSheetContent; export type Dependency = { uri: string; dependencies: Record; dependencyUriMaps: Record; content: DependencyContent; }; export type DependencyGraph = Record; ================================================ FILE: packages/paperclip-utils/src/core/memo.ts ================================================ import * as LRU from "lru-cache"; const DEFAULT_LRU_MAX = 10000; // need this for default arguments const getArgumentCount = (fn: (...args: any[]) => any) => { const str = fn.toString(); const params = str.match(/\(.*?\)|\w+\s*=>/)[0]; const args = params .replace(/[=>()]/g, "") .split(/\s*,\s*/) .filter(arg => arg.substr(0, 3) !== "..."); return args.length; }; export const memoize = any>( fn: TFunc, lruMax: number = DEFAULT_LRU_MAX, argumentCount: number = getArgumentCount(fn) ) => { if (argumentCount == Infinity || isNaN(argumentCount)) { throw new Error(`Argument count cannot be Infinity, 0, or NaN.`); } if (!argumentCount) { console.error(`Argument count should not be 0. Defaulting to 1.`); argumentCount = 1; } return compilFastMemoFn(argumentCount, lruMax > 0)( fn, new LRU({ max: lruMax }) ) as TFunc; }; export const shallowEquals = (a, b) => { const toa = typeof a; const tob = typeof b; if (toa !== tob) { return false; } if (toa !== "object" || !a || !b) { return a === b; } if (Object.keys(a).length !== Object.keys(b).length) { return false; } for (const key in a) { if (a[key] !== b[key]) { return false; } } return true; }; export const reuser = ( lruMax: number = DEFAULT_LRU_MAX, getKey: (value: TValue) => string, equals: (a: TValue, b: TValue) => boolean = shallowEquals ): ((value: TValue) => TValue) => { const cache = new LRU({ max: lruMax }); return (value: TValue) => { const key = getKey(value); if (!cache.has(key) || !equals(cache.get(key), value)) { cache.set(key, value); } return cache.get(key); }; }; const _memoFns: any = {}; const compilFastMemoFn = (argumentCount: number, acceptPrimitives: boolean) => { const hash = "" + argumentCount + acceptPrimitives; if (_memoFns[hash]) { return _memoFns[hash]; } const args = Array.from({ length: argumentCount }).map((v, i) => `arg${i}`); let buffer = ` return function(fn, keyMemo) { var memo = new WeakMap(); return function(${args.join(", ")}) { var currMemo = memo, prevMemo, key; `; for (let i = 0, n = args.length - 1; i < n; i++) { const arg = args[i]; buffer += ` prevMemo = currMemo; key = ${arg}; ${ acceptPrimitives ? `if ((typeof key !== "object" || !key) && !(key = keyMemo.get(${arg}))) { keyMemo.set(${arg}, key = {}); }` : "" } if (!(currMemo = currMemo.get(key))) { prevMemo.set(key, currMemo = new WeakMap()); } `; } const lastArg = args[args.length - 1]; buffer += ` key = ${lastArg}; ${ acceptPrimitives ? ` if ((typeof key !== "object" || !key) && !(key = keyMemo.get(${lastArg}))) { keyMemo.set(${lastArg}, key = {}); }` : "" } if (!currMemo.has(key)) { try { currMemo.set(key, fn(${args.join(", ")})); } catch(e) { throw e; } } return currMemo.get(key); }; }; `; return (_memoFns[hash] = new Function(buffer)()); }; /** * Calls target function once & proxies passed functions * @param fn */ export const underchange = any>( fn: TFunc ) => { let currentArgs = []; let ret: any; let started: boolean; const start = () => { if (started) { return ret; } started = true; return (ret = fn( ...currentArgs.map((a, i) => (...args) => currentArgs[i](...args)) )); }; return (((...args) => { currentArgs = args; return start(); }) as any) as TFunc; }; ================================================ FILE: packages/paperclip-utils/src/core/module-ast.ts ================================================ import { Sheet } from "../css/ast"; import { Node } from "../html/ast"; export enum ModuleKind { CSS = "CSS", PC = "PC" } type BaseModuleKind = { moduleKind: TKind; }; export type PCModule = Node & BaseModuleKind; export type CSSModule = Sheet & BaseModuleKind; export type Module = PCModule | CSSModule; ================================================ FILE: packages/paperclip-utils/src/core/promise.ts ================================================ const deferPromise = () => { let _resolve; const promise = new Promise(resolve => { _resolve = resolve; }); return [promise, _resolve]; }; ================================================ FILE: packages/paperclip-utils/src/core/report.ts ================================================ import { StringRange } from "../base/ast"; export type CoverageReport = { files: FileReport[]; }; export type ExpressionReport = { missingRanges: StringRange[]; count: number; }; export type FileReport = { uri: string; css: ExpressionReport; html: ExpressionReport; missingLines: number[]; lineCount: number; cssCount: number; htmlCount: number; }; ================================================ FILE: packages/paperclip-utils/src/core/resolve.ts ================================================ import * as path from "path"; import * as url from "url"; import { stripFileProtocol } from "./utils"; import { PC_CONFIG_FILE_NAME } from "./constants"; import { PaperclipConfig } from "./config"; export const resolveImportUri = (fs) => (fromPath: string, toPath: string) => { const filePath = resolveImportFile(fs)(fromPath, toPath); return filePath; }; export const resolveImportFile = (fs) => (fromPath: string, toPath: string) => { try { if (/\w+:\/\//.test(toPath)) { return toPath; } if (toPath.charAt(0) !== ".") { const uri = resolveModule(fs)(fromPath, toPath); if (!uri) { throw new Error(`module ${toPath} not found`); } return uri; } const uri = url.resolve(fromPath, toPath); // always want realpath here since file name is used as ID for PC files - need // to make sure that we're not doubling up on the same files. try { return url.pathToFileURL(fs.realpathSync(url.fileURLToPath(uri))).href; } catch (e) { return uri; } } catch (e) { return null; } }; const readJSONSync = (fs) => (uri: string) => JSON.parse(fs.readFileSync(uri, "utf8")); export const resolvePCConfig = (fs) => (fromPath: string): [PaperclipConfig, string] | null => { const configUrl = findPCConfigUrl(fs)(fromPath); if (!configUrl) return null; const uri = new URL(configUrl) as any; // need to parse each time in case config changed. return [readJSONSync(fs)(uri), configUrl]; }; const resolveModule = (fs) => (fromPath: string, moduleRelativePath: string) => { // need to parse each time in case config changed. const [config, configUrl] = resolvePCConfig(fs)(fromPath) || []; if (!config) { return null; } const configPathDir = path.dirname(stripFileProtocol(configUrl)); const moduleFileUrl = url.pathToFileURL( path.normalize( path.join(configPathDir, config.srcDir, moduleRelativePath) ) ); // FIRST look for modules in the sourceDirectory if (fs.existsSync(moduleFileUrl)) { // Need to follow symlinks return url.pathToFileURL(fs.realpathSync(moduleFileUrl)).href; } // No bueno? Move onto the module directories then if (config.moduleDirs) { const firstSlashIndex = moduleRelativePath.indexOf("/"); const moduleName = firstSlashIndex !== -1 ? moduleRelativePath.substring(0, firstSlashIndex) : moduleRelativePath; const srcPath = firstSlashIndex !== -1 ? moduleRelativePath.substring(firstSlashIndex) : ""; for (let i = 0, { length } = config.moduleDirs; i < length; i++) { const moduleDir = config.moduleDirs[i]; const moduleDirectory = path.join( resolveModuleDirectory(fs)(configPathDir, moduleDir), moduleName ); const modulePath = path.join(moduleDirectory, srcPath); if (fs.existsSync(modulePath)) { const actualPath = fs.realpathSync(modulePath); return url.pathToFileURL(actualPath).href; } } } return null; }; const resolveModuleDirectory = (fs) => (cwd: string, moduleDir: string) => { const c0 = moduleDir.charAt(0); if (c0 === "/" || c0 === ".") { return path.join(cwd, moduleDir); } let cdir = cwd; while (1) { const maybeDir = path.join(cdir, moduleDir); if (fs.existsSync(maybeDir)) { return maybeDir; } if (cdir === "/") { break; } cdir = path.dirname(cdir); } }; export const findPCConfigUrl = (fs) => (fromUri: string): string | null => { let cdir: string = stripFileProtocol(fromUri); // can't cache in case PC config was moved. while (1) { const configUrl = url.pathToFileURL(path.join(cdir, PC_CONFIG_FILE_NAME)); if (fs.existsSync(configUrl)) { return configUrl.href; } if (cdir === "/") { break; } cdir = path.dirname(cdir); } return null; }; const isntRoot = (cdir: string) => cdir !== "/" && cdir !== "." && !/^\w+:\\$/.test(cdir); ================================================ FILE: packages/paperclip-utils/src/core/url.ts ================================================ export const fileURLToPath = url => { if (typeof url !== "string") { url = url.href; } return url.substr("file://".length); }; export const pathToFileURL = filePath => { if (filePath.charAt(0) !== "/") { filePath = "/" + filePath; } return { href: `file://${filePath}` }; }; ================================================ FILE: packages/paperclip-utils/src/core/utils.ts ================================================ import * as url from "url"; export const stripFileProtocol = (filePath: string) => filePath.includes("file://") && url.fileURLToPath ? url.fileURLToPath(filePath) : filePath.replace("file://", ""); export const paperclipSourceGlobPattern = (dir: string) => dir === "." ? "**/*.pc" : dir + "/**/*.pc"; // TODO: we want to watch for CSS files here, but need to be // cognizant of generated CSS files which may clobber the PC engine. THe fix here // I think is to load GLOB data, _as well as_ resources loaded into the PC file. export const paperclipResourceGlobPattern = (dir: string) => dir === "." ? "**/*.{pc}" : dir + "/**/*.{pc}"; export const isGeneratedPaperclipFile = (filePath: string) => { return /\.(pc(.\w+)+|scoped.css)$/.test(filePath); }; export const isPaperclipFile = (filePath: string) => /\.pc$/.test(filePath); export const isPaperclipResourceFile = (filePath: string) => (isCSSFile(filePath) || isPaperclipFile(filePath)) && !isGeneratedPaperclipFile(filePath); export const isCSSFile = (filePath: string) => /\.css$/.test(filePath); export const getScopedCSSFilePath = (filePath: string) => filePath.replace(/\.css$/, ".scoped.css"); ================================================ FILE: packages/paperclip-utils/src/css/ast.ts ================================================ import { BasicRaws, StringRange } from "../base/ast"; import { Expression } from "../html/ast"; export type Sheet = { rules: Rule[]; raws: BasicRaws; declarations: StyleDeclaration[]; }; export enum RuleKind { Style = "Style", Charset = "Charset", Namespace = "Namespace", Include = "Include", Comment = "Comment", FontFace = "FontFace", Media = "Media", Mixin = "Mixin", Export = "Export", Supports = "Supports", Page = "Page", Document = "Document", Keyframes = "Keyframes", Keyframe = "Keyframe", } type BaseRule = { id: string; ruleKind: TKind; range: StringRange; }; export enum SelectorKind { Group = "Group", Combo = "Combo", Descendent = "Descendent", This = "This", Within = "Within", Global = "Global", Prefixed = "Prefixed", PseudoElement = "PseudoElement", PseudoParamElement = "PseudoParamElement", Not = "Not", Child = "Child", Adjacent = "Adjacent", Sibling = "Sibling", Id = "Id", Element = "Element", Attribute = "Attribute", Class = "Class", AllSelector = "AllSelector", } export type BaseSelector = { id: string; selectorKind: TKind; range: StringRange; }; type GroupSelector = { selectors: Selector[]; } & BaseSelector; type WithinSelector = { selector: Selector; } & BaseSelector; type GlobalSelector = { selector: Selector; } & BaseSelector; type SelfSelector = { selector: Selector; } & BaseSelector; type PrefixedSelector = { connector: string; postfixSelector?: Selector; } & BaseSelector; type ComboSelector = { selectors: Selector[]; } & BaseSelector; type DescendentSelector = { ancestor: Selector; descendent: Selector; } & BaseSelector; type PseudoElementSelector = { separator: string; target: Selector; name: string; } & BaseSelector; type PseudoParamElementSelector = { target: Selector; name: string; param: string; } & BaseSelector; type NotSelector = { selector: Selector; } & BaseSelector; type ChildSelector = { parent: Selector; child: Selector; } & BaseSelector; type AdjacentSelector = { selector: Selector; nextSiblingSelector: Selector; } & BaseSelector; type SiblingSelector = { selector: Selector; siblingSelector: Selector; } & BaseSelector; type IdSelector = { id: string; } & BaseSelector; type ElementSelector = { tagName: string; } & BaseSelector; type AttributeSelector = { operator?: string; name: string; value?: string; } & BaseSelector; type ClassSelector = { className: string; } & BaseSelector; type AllSelector = BaseSelector; export type Selector = | GroupSelector | WithinSelector | GlobalSelector | PrefixedSelector | ComboSelector | DescendentSelector | SelfSelector | PseudoElementSelector | PseudoParamElementSelector | NotSelector | ChildSelector | AdjacentSelector | SiblingSelector | IdSelector | ElementSelector | AttributeSelector | ClassSelector | AllSelector; export enum StyleDeclarationKind { KeyValue = "KeyValue", Include = "Include", Media = "Media", Content = "Content", } type BaseStyleDeclaration = { id: string; declarationKind: TKind; range: StringRange; }; export type KeyValueDeclaration = { name: string; value: string; range: StringRange; nameRange: StringRange; valueRange: StringRange; raws: BasicRaws; } & BaseStyleDeclaration; export type MediaDeclaration = ConditionShape & BaseStyleDeclaration; export type Include = BaseInclude & BaseStyleDeclaration; export type Content = { raws: BasicRaws; } & BaseStyleDeclaration; export type IncludeReference = { id: string; parts: IncludePart[]; range: StringRange; }; export type IncludePart = { id: string; name: string; range: StringRange; }; export type StyleDeclaration = | KeyValueDeclaration | Include | MediaDeclaration | Content; export type StyleRule = { range: StringRange; selector: Selector; declarations: StyleDeclaration[]; children: StyleRule[]; raws: BasicRaws; } & BaseRule; /* #[derive(Debug, PartialEq, Serialize, Clone)] pub struct KeyframeRule { pub key: String, pub declarations: Vec, pub range: StringRange, } */ export type KeyframeRule = { key: string; raws: BasicRaws; declarations: StyleDeclaration[]; range: StringRange; } & BaseRule; /* #[derive(Debug, PartialEq, Serialize, Clone)] pub struct KeyframesRule { pub name: String, pub rules: Vec, pub range: StringRange, }*/ export type KeyframesRule = { name: string; rules: KeyframeRule[]; raws: BasicRaws; range: StringRange; } & BaseRule; type ConditionShape = { name: string; conditionText: string; rules: ChildRule[]; raws: BasicRaws; range: StringRange; declarations: StyleDeclaration[]; }; type BaseConditionRule = ConditionShape & BaseRule; type MediaRule = BaseConditionRule; export type CommentRule = { value: string; } & BaseRule; type FontFaceRule = { declarations: StyleDeclaration[]; raws: BasicRaws; } & BaseRule; type CharsetRule = { value: string; raws: BasicRaws; } & BaseRule; type BaseInclude = { mixinName: IncludeReference; range: StringRange; declarations: StyleDeclaration[]; rules: StyleRule[]; raws: BasicRaws; }; type IncludeRule = BaseInclude & BaseRule; export type MixinRule = { name: MixinName; raws: BasicRaws; declarations: StyleDeclaration[]; range: StringRange; rules: StyleRule[]; } & BaseRule; export type MixinName = { id: string; value: string; range: StringRange; }; export type ExportRule = { rules: Rule[]; range: StringRange; raws: BasicRaws; } & BaseRule; export type ConditionRule = MediaRule; export type ChildRule = any; export type Rule = | StyleRule | ConditionRule | MixinRule | ExportRule | CharsetRule | CommentRule | MediaRule | FontFaceRule | IncludeRule | KeyframesRule | KeyframeRule; export type StyleExpression = | Rule | Include | MixinName | IncludePart | IncludeReference | StyleDeclaration | Selector; export const getSheetClassNames = ( sheet: Sheet, allClassNames: string[] = [] ) => { return getRulesClassNames(sheet.rules, allClassNames); }; const getRulesClassNames = (rules: Rule[], allClassNames: string[] = []) => { for (const rule of rules) { getRuleClassNames(rule, allClassNames); } return allClassNames; }; export const getRuleClassNames = (rule: Rule, allClassNames: string[] = []) => { switch (rule.ruleKind) { case RuleKind.Media: { getRulesClassNames(rule.rules, allClassNames); break; } case RuleKind.Style: { getSelectorClassNames(rule.selector, allClassNames); break; } } return allClassNames; }; export const traverseSheet = ( sheet: Sheet, owner: Expression, each: (rule: StyleExpression, parent: Expression) => void ) => { return ( traverseStyleExpressions(sheet.declarations, owner, each) && traverseStyleExpressions(sheet.rules, owner, each) ); }; type TraverseEach = ( rule: StyleExpression, parent: Expression ) => void | boolean; const traverseChildren = ( traverse: ( rule: StyleExpression, owner: Expression, each: TraverseEach ) => boolean ) => (rules: StyleExpression[], owner: Expression, each: TraverseEach) => { for (const rule of rules) { if (!traverse(rule, owner, each)) { return false; } } return true; }; export const isRule = (expression: StyleExpression): expression is Rule => { return RuleKind[(expression as Rule).ruleKind] != null; }; export const isStyleDeclaration = ( expression: Expression ): expression is StyleDeclaration => { return ( StyleDeclarationKind[(expression as StyleDeclaration).declarationKind] != null ); }; export const isMaybeStyleSheet = (expression): expression is Sheet => expression.rules != null && expression.rules != null && expression.raws != null; export const isStyleObject = (expression): expression is StyleExpression => { return ( expression.rules != null || isStyleDeclaration(expression) || isRule(expression) || isStyleSelector(expression) ); }; export const isStyleSelector = (expression): expression is Selector => { return SelectorKind[(expression as Selector).selectorKind] != null; }; export const isSelector = (expression): expression is Selector => { return SelectorKind[expression.kind] != null; }; export const isIncludePart = ( expression: Expression ): expression is IncludePart => { return (expression as any).name != null; }; export const traverseStyleExpression = ( rule: StyleExpression, owner: Expression, each: (rule: StyleExpression, parent: Expression) => void | boolean ) => { if (each(rule, owner) === false) { return false; } if (isRule(rule)) { switch (rule.ruleKind) { case RuleKind.Media: { return traverseChildren(traverseStyleExpression)( rule.rules, rule, each ); } case RuleKind.Export: { return traverseStyleExpressions(rule.rules, rule, each); } case RuleKind.Style: { return traverseStyleRule(rule, each); } case RuleKind.Mixin: { return traverseStyleExpressions(rule.declarations, rule, each); } case RuleKind.Keyframes: { return traverseChildren((child: KeyframeRule) => { return traverseStyleExpressions(child.declarations, child, each); })(rule.rules, rule, each); } } } else if (isStyleDeclaration(rule)) { switch (rule.declarationKind) { case StyleDeclarationKind.Include: { each(rule.mixinName, rule); for (const part of rule.mixinName.parts) { if (!traverseStyleExpression(part, rule.mixinName, each)) { return false; } } return true; } } } return true; }; const traverseStyleExpressions = traverseChildren(traverseStyleExpression); const traverseStyleRule = ( rule: StyleRule, each: (rule: StyleExpression, parent: Expression) => void | boolean ) => traverseStyleExpressions(rule.declarations, rule, each) && traverseChildren(traverseStyleExpression)(rule.children, rule, each); export const getSelectorClassNames = ( selector: Selector, allClassNames: string[] = [] ) => { switch (selector.selectorKind) { case SelectorKind.Combo: case SelectorKind.Group: { for (const child of selector.selectors) { getSelectorClassNames(child, allClassNames); } break; } case SelectorKind.Descendent: { getSelectorClassNames(selector.ancestor, allClassNames); getSelectorClassNames(selector.descendent, allClassNames); break; } case SelectorKind.PseudoElement: { getSelectorClassNames(selector.target, allClassNames); break; } case SelectorKind.PseudoParamElement: { getSelectorClassNames(selector.target, allClassNames); break; } case SelectorKind.Not: { getSelectorClassNames(selector.selector, allClassNames); break; } case SelectorKind.Child: { getSelectorClassNames(selector.parent, allClassNames); getSelectorClassNames(selector.child, allClassNames); break; } case SelectorKind.Adjacent: { getSelectorClassNames(selector.selector, allClassNames); getSelectorClassNames(selector.nextSiblingSelector, allClassNames); break; } case SelectorKind.Sibling: { getSelectorClassNames(selector.selector, allClassNames); getSelectorClassNames(selector.siblingSelector, allClassNames); break; } case SelectorKind.Class: { allClassNames.push(selector.className); break; } } return allClassNames; }; ================================================ FILE: packages/paperclip-utils/src/css/decl-value-ast.ts ================================================ export type DeclValueRoot = { important: boolean; value: RootValue; }; export enum DeclRootValueKind { List = "List", Group = "Group", Value = "Value", } export type BaseRootValue = { expressionKind: TKind; }; export type List = { items: ListItem[]; } & BaseRootValue; export type ListItem = Group | Value; export enum ListItemKind { Group = "Group", Value = "Value", } export type BaseListItem = { listItemKind: TKind; }; export type Group = { parameters: Value[]; } & BaseListItem & BaseRootValue; export enum ValueKind { Dimension = "Dimension", Operation = "Operation", Number = "Number", String = "Str", Raw = "Raw", Hex = "Hex", Keyword = "Keyword", FunctionCall = "FunctionCall", } type BaseValue = { valueKind: TKind; } & BaseListItem & BaseRootValue; export type Dimension = { value: Num; unit: string; } & BaseValue; export type Operation = { left: Value; operation: string; right: Value; } & BaseValue; export type Num = { value: string; } & BaseValue; export type Str = { value: string; } & BaseValue; export type Raw = { value: string; } & BaseValue; export type Hex = { value: string; } & BaseValue; export type Keyword = { value: string; } & BaseValue; export type FunctionCall = { name: string; parameters: List; } & BaseValue; export type Value = | Dimension | Operation | Num | Str | Raw | Hex | Keyword | FunctionCall; export type RootValue = List | Group | Value; ================================================ FILE: packages/paperclip-utils/src/css/exports.ts ================================================ export type CSSExports = { mixins: Record; classNames: Record; variables: Record; keyframes: Record; }; export type ClassNameExport = { name: string; scopedName: string; public: boolean; }; export type MixinExport = { // declarations: any; public: boolean; }; export type VariableExport = { name: string; value: string; source: any; }; export type KeyframesExport = { name: string; public: boolean; source: any; }; export const isCSSExports = (exports: any): exports is CSSExports => { return exports.mixins != null; }; ================================================ FILE: packages/paperclip-utils/src/css/patcher.ts ================================================ export const patchCSSSheet = (sheet: any, mutations: any[]) => { let newSheet = sheet; for (const mutation of mutations) { switch (mutation.action.kind) { case "DeleteRule": { const rules = [ ...newSheet.rules.slice(0, mutation.action.index), ...newSheet.rules.slice(mutation.action.index + 1) ]; newSheet = { ...newSheet, rules }; break; } case "InsertRule": { const rules = [ ...newSheet.rules.slice(0, mutation.action.index), mutation.action.rule, ...newSheet.rules.slice(mutation.action.index) ]; newSheet = { ...newSheet, rules }; break; } case "ReplaceRule": { const rules = [ ...newSheet.rules.slice(0, mutation.action.index), mutation.action.rule, ...newSheet.rules.slice(mutation.action.index + 1) ]; newSheet = { ...newSheet, rules }; break; } } } return newSheet; }; ================================================ FILE: packages/paperclip-utils/src/css/stringify-sheet.ts ================================================ import * as path from "path"; import { relative, resolve } from "path"; import * as url from "url"; export type StringifySheetOptions = { resolveUrl?: (url: string) => string; uri?: string; }; export const stringifyCSSSheet = ( sheet, options: StringifySheetOptions = {} ) => { return sheet.rules .map((rule) => stringifyCSSRule(rule, options)) .join("\n\n"); }; export const stringifyCSSRule = (rule, options: StringifySheetOptions = {}) => { switch (rule.kind) { case "Charset": return `@charset "${rule.value}"; `; case "Style": return stringifyStyleRule(rule, options); case "Page": case "Supports": case "Media": return stringifyConditionRule(rule, options); case "FontFace": return stringifyFontFaceRule(rule, options); case "Keyframes": return stringifyKeyframesRule(rule, options); } }; const stringifyConditionRule = ( { name, conditionText, rules }, options: StringifySheetOptions ) => { return `@${name} ${conditionText} {\n${rules .map((style) => stringifyCSSRule(style, options)) .join("\n")}\n}`; }; const stringifyKeyframesRule = ( { name, rules }, options: StringifySheetOptions ) => { return `@keyframes ${name} {\n${rules .map((style) => stringifyKeyframeRule(style, options)) .join("\n")}\n}`; }; const stringifyKeyframeRule = ( { key, style }, options: StringifySheetOptions ) => { return `${key} {\n${style .map((style) => stringifyStyle(style, options)) .join("\n")}\n}`; }; const stringifyFontFaceRule = ({ style }, options: StringifySheetOptions) => { return `@font-face {\n${style .map((style) => stringifyStyle(style, options)) .join("\n")}\n}`; }; const stringifyStyleRule = ( { selectorText, style, ...rest }, options: StringifySheetOptions ) => { return `${selectorText} {\n${style .map((style) => stringifyStyle(style, options)) .join("\n")}\n}`; }; const stringifyStyle = ( { name, value }, { uri, resolveUrl }: StringifySheetOptions ) => { if (value) { // required for bundling, otherwise file protocol is maintained if (uri) { const urls = value.match(/(file:\/\/.*?)(?=['")])/g) || []; const selfPathname = url.fileURLToPath(uri); for (const foundUrl of urls) { const pathname = url.fileURLToPath(foundUrl); let relativePath = path.relative(path.dirname(selfPathname), pathname); if (relativePath.charAt(0) !== ".") { relativePath = "./" + relativePath; } value = value.replace(foundUrl, relativePath); } } if (value && resolveUrl) { if (value.includes("file:")) { const url = value.match(/(file:\/\/[^)]+)/)[1]; value = value.replace(url, resolveUrl(url)); } else if (value.includes("url(")) { const parts = value.match(/url\(['"]?(.*?)['"]?\)/); const url = parts && parts[1]; if (url && !url.includes("http")) { value = value.replace(url, resolveUrl(url)); } } } } return ` ${name.trim()}:${value};`; }; ================================================ FILE: packages/paperclip-utils/src/css/style-inspection.ts ================================================ // 1:1 with Rust import { memoize } from "../core/memo"; import { DeclValueRoot, RootValue } from "./decl-value-ast"; export enum SelectorScopeKind { Element = "Element", Document = "Document", } type BaseSelectorScope = { kind: TKind; }; export type ElementSelectorScope = BaseSelectorScope; export type DocumentSelectorScope = BaseSelectorScope; export type SelectorScope = ElementSelectorScope | DocumentSelectorScope; export enum SelectorInfoKind { List = "list", All = "All", Element = "Element", PseudoElement = "PseudoElement", PsuedoParamElement = "PseudoParamElement", Attribute = "Attribute", Not = "Not", Id = "Id", Class = "Class", Combo = "Combo", Child = "Child", Descendent = "Descendent", Adjacent = "Adjacent", Sibling = "Sibling", } export type BaseSelectorInfo = { kind: TKind; }; export type GroupSelectorInfo = { selectors: Array>; } & BaseSelectorInfo; export type TargetSelectorInfo = { value: string; } & BaseSelectorInfo; export type WrapperSelectorInfo = { selector: BaseSelectorInfo; } & BaseSelectorInfo; export type BinarySelectorInfo = { left: BaseSelectorInfo; right: BaseSelectorInfo; } & BaseSelectorInfo; export type ListSelectorInfo = GroupSelectorInfo; export type ElementSelectorInfo = TargetSelectorInfo; export type AllSelectorInfo = GroupSelectorInfo; export type PseudoElementSelectorInfo = TargetSelectorInfo; export type PseudoParamElementSelectorInfo = TargetSelectorInfo; export type AttributeSelectorInfo = TargetSelectorInfo; export type NotSelectorInfo = WrapperSelectorInfo; export type IdSelectorInfo = TargetSelectorInfo; export type ClassSelectorInfo = { name: string; value: string; scope: SelectorScope; } & BaseSelectorInfo; export type ComboSelectorInfo = GroupSelectorInfo; export type ChildSelectorInfo = BinarySelectorInfo; export type DescendentSelectorInfo = BinarySelectorInfo; export type AdjacentSelectorInfo = BinarySelectorInfo; export type SiblingSelectorInfo = BinarySelectorInfo; export type SelectorInfo = | ListSelectorInfo | ElementSelectorInfo | AllSelectorInfo | PseudoElementSelectorInfo | PseudoParamElementSelectorInfo | AttributeSelectorInfo | NotSelectorInfo | IdSelectorInfo | ClassSelectorInfo | ComboSelectorInfo | ChildSelectorInfo | DescendentSelectorInfo | AdjacentSelectorInfo | SiblingSelectorInfo; export type StyleDeclarationInfo = { sourceId: string; name: string; rawValue: string; value: DeclValueRoot; active: boolean; }; export type MediaInfo = { conditionText: string; active: boolean; }; export type StyleRuleInfo = { selectorText: string; inherited: boolean; selectorInfo: SelectorInfo; pseudoElementName?: string; sourceId: string; sourceUri: string; media?: MediaInfo; declarations: StyleDeclarationInfo[]; specificity: number; }; export type NodeStyleInspection = { styleRules: StyleRuleInfo[]; }; export type ComputedDeclarationInfo = { name: string; rawValue: string; value: DeclValueRoot; variable?: boolean; sourceRules: StyleRuleInfo[]; }; export type SquashedStyleInspection = Record; const INHERITED_DECLS = [ "border-collapse", "border-spacing", "caption-side", "color", "cursor", "direction", "empty-cells", "font-family", "font-size", "font-style", "font-variant", "font-weight", "font-size-adjust", "font-stretch", "font", "letter-spacing", "line-height", "list-style-image", "list-style-position", "list-style-type", "list-style", "orphans", "quotes", "tab-size", "text-align", "text-align-last", "text-decoration-color", "text-indent", "text-justify", "text-shadow", "text-transform", "visibility", "white-space", "widows", "word-break", "word-spacing", "word-wrap", ]; export const squashInspection = memoize( (inspection: NodeStyleInspection): ComputedDeclarationInfo[] => { const squashed: Array = []; const used: Record = {}; for (const rule of inspection.styleRules) { for (const declaration of rule.declarations) { if (rule.inherited && !INHERITED_DECLS.includes(declaration.name)) { continue; } if (!used[declaration.name]) { squashed.push( (used[declaration.name] = { name: declaration.name, variable: declaration.name.indexOf("--") === 0, value: declaration.value, rawValue: declaration.rawValue, sourceRules: [], }) ); } used[declaration.name].sourceRules.push(rule); } } return squashed; } ); ================================================ FILE: packages/paperclip-utils/src/css/virt-mutation.ts ================================================ import { VirtRule } from "./virt"; /* import { ExprTextSource } from "./base-virt"; import { VirtScriptObject } from "./js-virt"; import { VirtualElementSourceInfo, VirtualNode } from "./virt"; export enum ActionKind { ReplaceNode = "ReplaceNode", InsertChild = "InsertChild", DeleteChild = "DeleteChild", SetAttribute = "SetAttribute", SetElementSourceInfo = "SetElementSourceInfo", SetAnnotations = "SetAnnotations", SourceUriChanged = "SourceUriChanged", SetText = "SetText", RemoveAttribute = "RemoveAttribute" } export type BaseAction = { kind: TKind; }; export type ReplaceNode = { replacement: VirtualNode; } & BaseAction; export type InsertChild = { child: VirtualNode; index: number; } & BaseAction; export type DeleteChild = { index: number; } & BaseAction; export type SetAttribute = { name: string; value?: string; } & BaseAction; export type SetElementSourceInfo = { value: VirtualElementSourceInfo; } & BaseAction; export type SetAnnotations = { value?: VirtScriptObject; } & BaseAction; export type RemoveAttribute = { name: string; } & BaseAction; export type SetText = { value: string; } & BaseAction; export type Action = | ReplaceNode | InsertChild | DeleteChild | SetElementSourceInfo | SetAttribute | SetAnnotations | SetText | RemoveAttribute; export type Mutation = { nodePath: number[]; action: Action; }; */ // use super::virt::Rule; // use crate::base::ast::ExprTextSource; // use serde::Serialize; // #[derive(Debug, PartialEq, Serialize, Clone)] // pub struct SetSelectorText { // pub selector_text: String, // } // #[derive(Debug, PartialEq, Serialize, Clone)] // pub struct InsertRule { // pub index: usize, // pub rule: Rule, // } // #[derive(Debug, PartialEq, Serialize, Clone)] // pub struct ReplaceRule { // pub index: usize, // pub rule: Rule, // } // #[derive(Debug, PartialEq, Serialize, Clone)] // pub struct DeleteRule { // pub index: usize, // } // #[derive(Debug, PartialEq, Serialize, Clone)] // pub struct SetDeclaration { // pub name: String, // pub value: String, // } // #[derive(Debug, PartialEq, Serialize, Clone)] // #[serde(tag = "kind")] // pub enum Action { // InsertRule(InsertRule), // DeleteRule(DeleteRule), // ReplaceRule(ReplaceRule), // SetDeclaration(SetDeclaration), // SetSelectorText(SetSelectorText), // } // #[derive(Debug, PartialEq, Serialize, Clone)] // pub struct Mutation { // pub path: Vec, // pub action: Action, // } export enum CSSMutationActionKind { InsertRule = "InsertRule", DeleteRule = "DeleteRule", ReplaceRule = "ReplaceRule", SetDeclaration = "SetDeclaration", SetSelectorText = "SetSelectorText" } export type BaseCSSAction = { kind: TKind; }; export type InsertRule = { index: number; rule: VirtRule; }; export type DeleteRule = { index: number; }; export type ReplaceRule = { index: number; rule: VirtRule; }; export type SetDeclaration = { name: string; value: string; }; export type SetSelectorText = { selectorText: string; }; export type CSSAction = | InsertRule | DeleteRule | ReplaceRule | SetDeclaration | SetSelectorText; export type CSSMutation = { path: number[]; action: CSSAction; }; ================================================ FILE: packages/paperclip-utils/src/css/virt.ts ================================================ export type VirtSheet = { rules: VirtRule[]; }; export enum VirtRuleKind { Style = "Style", Media = "Media", FontFace = "FontFace" } export type BaseVirtRule = { kind: TKind; }; export type VirtDeclaration = { name: string; value: string; sourceId: string; }; export type VirtFontFace = { style: VirtDeclaration[]; } & BaseVirtRule; export type VirtStyleRule = { selectorText: string; style: VirtDeclaration[]; } & BaseVirtRule; export type MediaRule = { rules: VirtRule[]; } & BaseVirtRule; export type VirtRule = VirtStyleRule | MediaRule | VirtStyleRule; ================================================ FILE: packages/paperclip-utils/src/html/ast.ts ================================================ import { ScriptExpression, ScriptExpressionKind, Reference, traverseJSExpression, } from "../script/ast"; import { Sheet, traverseSheet, MixinRule, RuleKind, isRule, StyleExpression, } from "../css/ast"; import { BasicRaws, StringRange } from "../base/ast"; import { flattenTreeNode, getNodePath, getTreeNodeMap } from "./tree"; import * as crc32 from "crc32"; import { resolveImportFile } from "../core/resolve"; import * as path from "path"; import { LOGIC_TAG_NAME, DEFAULT_PART_ID, AS_ATTR_NAME, PREVIEW_ATTR_NAME, } from "../core/constants"; import { memoize } from "../core/memo"; import { DependencyGraph, DependencyNodeContent } from "../core/graph"; import { StringLiteral } from "../core/ast"; export enum NodeKind { Fragment = "Fragment", Text = "Text", Annotation = "Annotation", Comment = "Comment", Element = "Element", StyleElement = "StyleElement", Slot = "Slot", } export type BaseNode = { id: string; nodeKind: TKind; }; // TODO - include location here. export type Text = StringLiteral & BaseNode; export type Annotation = { properties: AnnotationProperty[]; range: StringRange; } & BaseNode; export declare type Comment = { raws: BasicRaws; value: string; annotation: Annotation; range: StringRange; } & BaseNode; export enum AnnotationPropertyKind { Text = "Text", Declaration = "Declaration", } type BaseAnnotationProperty = { kind: TKind; }; export type TextAnnotation = { value: string; raws: BasicRaws; } & BaseAnnotationProperty; export type DeclarationAnnotation = { name: string; value: ScriptExpression; raws: BasicRaws; } & BaseAnnotationProperty; export type AnnotationProperty = TextAnnotation | DeclarationAnnotation; export type ElementRaws = { before: string; }; export type Element = { id: string; range: StringRange; raws: ElementRaws; // TODO - change this to OpenTag. Don't keep location here openTagRange: StringRange; // TODO - change this to ElementTagName. name should go in value tagNameRange: StringRange; tagName: string; attributes: Attribute[]; value: string; children: Node[]; } & BaseNode; export type StyleElement = { sheet: Sheet; range: StringRange; raws: BasicRaws; } & BaseNode; export enum AttributeKind { ShorthandAttribute = "ShorthandAttribute", KeyValueAttribute = "KeyValueAttribute", SpreadAttribute = "SpreadAttribute", PropertyBoundAttribute = "PropertyBoundAttribute", } type BaseAttribute = { attrKind: TKind; }; type ShorthandAttribute = { id: string; reference: ScriptExpression; range: StringRange; } & BaseAttribute; type SpreadAttribute = { id: string; script: ScriptExpression; range: StringRange; } & BaseAttribute; type KeyValueAttribute = { id: string; name: string; value?: AttributeValue; range: StringRange; } & BaseAttribute; export type PropertyBoundAttribute = { id: string; name: string; bindingName: string; value: AttributeValue; range: StringRange; } & BaseAttribute; export type Attribute = | ShorthandAttribute | SpreadAttribute | KeyValueAttribute | PropertyBoundAttribute; export enum AttributeValueKind { DyanmicString = "DyanmicString", String = "String", Slot = "Slot", } export type BaseAttributeValue = { id: string; attrValueKind: TKind; }; export type StringAttributeValue = StringLiteral & BaseAttributeValue; export enum DynamicStringAttributeValuePartKind { Literal = "Literal", ClassNamePierce = "ClassNamePierce", Slot = "Slot", } type BaseDynamicStringAttributeValuePart< TPartKind extends DynamicStringAttributeValuePartKind > = { id: string; partKind: TPartKind; }; type DynamicStringLiteralPart = { range: StringRange; value: string; } & BaseDynamicStringAttributeValuePart; type DynamicStringClassNamePiercePart = { range: StringRange; className: string; } & BaseDynamicStringAttributeValuePart; type DynamicStringSlotPart = ScriptExpression & BaseDynamicStringAttributeValuePart; export type DynamicStringAttributeValuePart = | DynamicStringLiteralPart | DynamicStringClassNamePiercePart | DynamicStringSlotPart; export type DynamicStringAttributeValue = { values: DynamicStringAttributeValuePart[]; range: StringRange; } & BaseAttributeValue; export type SlotAttributeValue = { script: ScriptExpression; range: StringRange; } & BaseAttributeValue; export type AttributeValue = | StringAttributeValue | SlotAttributeValue | DynamicStringAttributeValue; export type Fragment = { value: string; children: Node[]; range: StringRange; } & BaseNode; export type Slot = { script: ScriptExpression; range: StringRange; raws: BasicRaws; } & BaseNode; export type Node = | Text | Element | StyleElement | Fragment | Slot | Annotation | Comment; export type Expression = | Node | Attribute | AttributeValue | StyleExpression | ScriptExpression | DynamicStringAttributeValuePart | StringLiteral; export enum RootExpressionKind { String = "String", Node = "Node", Attribute = "Attribute", CSS = "CSS", Script = "Script", } type BaseRootExpression = { pcObjectKind: TKind; }; export type RootString = StringLiteral & BaseRootExpression; export type RootNode = Node & BaseRootExpression; export type RootAttribute = Node & BaseRootExpression; export type RootCSS = StyleExpression & BaseRootExpression; export type RootScript = ScriptExpression & BaseRootExpression; export type RootExpression = | RootString | RootNode | RootCSS | RootScript | RootAttribute; const a: AttributeValue = null; export const getImports = (ast: Node): Element[] => getChildrenByTagName("import", ast).filter((child) => { return hasAttribute("src", child); }); export const getRelativeFilePath = (fs) => (fromFilePath: string, importFilePath: string) => { const logicPath = resolveImportFile(fs)(fromFilePath, importFilePath); let relativePath = path.relative(path.dirname(fromFilePath), logicPath); if (relativePath.charAt(0) !== ".") { relativePath = `./${relativePath}`; } return relativePath; }; export const getImportIds = (ast: Node): string[] => getImports(ast) .map((node) => getAttributeStringValue(AS_ATTR_NAME, node)) .filter(Boolean) as string[]; export const getImportById = (id: string, ast: Node): Element | null => getImports(ast).find((imp) => { return getAttributeStringValue(AS_ATTR_NAME, imp) === id; }); export const getImportBySrc = (src: string, ast: Node): Element | null => getImports(ast).find((imp) => { return getAttributeStringValue("src", imp) === src; }); export const getChildren = (ast: Node): Node[] => { if (ast.nodeKind === NodeKind.Element || ast.nodeKind === NodeKind.Fragment) { return ast.children; } return []; }; export const getStyleScopeId = (filePath: string) => { if (filePath.indexOf("file://") !== 0) { filePath = "file://" + filePath; } return crc32(filePath); }; export const getChildrenByTagName = (tagName: string, parent: Node) => getChildren(parent).filter((child) => { return child.nodeKind === NodeKind.Element && child.tagName === tagName; }) as Element[]; export const findByNamespace = ( namespace: string, current: Node | ScriptExpression, allChildrenByNamespace: Element[] = [] ) => { if (isNode(current)) { if (current.nodeKind === NodeKind.Element) { if (current.tagName.split(".")[0] === namespace) { allChildrenByNamespace.push(current); } } for (const child of getChildren(current)) { findByNamespace(namespace, child, allChildrenByNamespace); } if (current.nodeKind === NodeKind.Element) { for (const attribute of current.attributes) { if ( attribute.attrKind === AttributeKind.KeyValueAttribute && attribute.value ) { if ( attribute.value.attrValueKind === AttributeValueKind.Slot && attribute.value.script.scriptKind === ScriptExpressionKind.Node ) { findByNamespace( namespace, attribute.value.script, allChildrenByNamespace ); } } } } if (current.nodeKind === NodeKind.Slot) { findByNamespace(namespace, current.script, allChildrenByNamespace); } } else if (isScriptExpression(current)) { if (current.scriptKind === ScriptExpressionKind.Conjunction) { findByNamespace(namespace, current.left, allChildrenByNamespace); findByNamespace(namespace, current.right, allChildrenByNamespace); } } return allChildrenByNamespace; }; export const getMetaValue = (name: string, root: Node) => { const metaElement = getChildrenByTagName("meta", root).find( (meta) => hasAttribute("src", meta) && getAttributeStringValue("name", meta) === name ); return metaElement && getAttributeStringValue("content", metaElement); }; export const getAttribute = (name: string, element: Element) => element.attributes.find((attr) => { return ( attr.attrKind === AttributeKind.KeyValueAttribute && attr.name === name ); }) as KeyValueAttribute; export const getAttributeValue = (name: string, element: Element) => { const attr = getAttribute(name, element); return attr && attr.value; }; export const getAttributeStringValue = (name: string, element: Element) => { const value = getAttributeValue(name, element); return ( value && value.attrValueKind === AttributeValueKind.String && value.value ); }; export const getStyleElements = (ast: Node): StyleElement[] => { const styleElements: StyleElement[] = []; traverseExpression(ast, null, (node: Node) => { if (node.nodeKind === NodeKind.StyleElement) { styleElements.push(node); } }); return styleElements; }; export const isVisibleElement = (ast: Element): boolean => { return !/^(import|logic|meta|style|part|preview)$/.test(ast.tagName); }; export const isVisibleNode = (node: Node): boolean => node.nodeKind === NodeKind.Text || node.nodeKind === NodeKind.Fragment || node.nodeKind === NodeKind.Slot || (node.nodeKind === NodeKind.Element && isVisibleElement(node)); export const getVisibleChildNodes = (ast: Node): Node[] => getChildren(ast).filter(isVisibleNode); export const isComponent = (node: Node): node is Element => node.nodeKind === NodeKind.Element && hasAttribute("component", node) && hasAttribute(AS_ATTR_NAME, node); export const isImport = (node: Node): node is Element => node.nodeKind === NodeKind.Element && node.tagName === "import" && hasAttribute("src", node); export const getParts = (ast: Node): Element[] => getChildren(ast).filter(isComponent) as Element[]; export const getPartIds = (ast: Node): string[] => getParts(ast) .map((node) => getAttributeStringValue(AS_ATTR_NAME, node)) .filter(Boolean) as string[]; export const getDefaultPart = (ast: Node): Element => getParts(ast).find( (part) => getAttributeStringValue(AS_ATTR_NAME, part) === DEFAULT_PART_ID ); export const getLogicElement = (ast: Node): Element | null => { return getChildren(ast).find( (child) => child.nodeKind === NodeKind.Element && child.tagName === LOGIC_TAG_NAME ) as Element; }; export const hasAttribute = (name: string, element: Element) => getAttribute(name, element) != null; // https://github.com/crcn/tandem/blob/10.0.0/packages/common/src/state/tree.ts#L137 // TODO export const getParentNode = (node: Node, root: Node) => { const nodePath = getNodePath(node, root).split("."); nodePath.pop(); const map = getTreeNodeMap(root); return map[nodePath.join(".")] as Fragment | Element; }; export const getPCNodeAnnotations = (node: Node, root: Node) => { const parent = getParentNode(node, root); const prevChild = parent.children[parent.children.indexOf(node) - 1]; if (prevChild?.nodeKind === NodeKind.Comment) { return prevChild; } return null; }; export const getNodeById = memoize((nodeId: string, root: Node) => { return flattenTreeNode(root).find((desc) => desc.id === nodeId); }); export const isComponentInstance = ( node: Expression, root: Node ): node is Element => { if (!isNode(node) || node.nodeKind !== NodeKind.Element) { return false; } const importIds = getImportIds(root); const internalComponents = getComponentMap(root); return ( importIds.includes(node.tagName.split(".").shift()) || internalComponents[node.tagName] != null ); }; export const getDocumentComponents = (root: Node) => (root.nodeKind === NodeKind.Fragment ? root.children : [root]).filter( isComponent ); export const getComponentMap = memoize( (root: Node): Record => getDocumentComponents(root).reduce((map, element) => { map[getAttributeStringValue("as", element)] = element; return map; }, {}) ); export const getInstanceComponentInfo = ( instance: Element, uri: string, graph: DependencyGraph ): [string, Element] => { const entry = graph[uri]; const components = getComponentMap(entry.content as DependencyNodeContent); if (components[instance.tagName]) { const component = components[instance.tagName]; return [uri, component]; } else { const parts = instance.tagName.split("."); const depUri = entry.dependencies[parts.shift()]; if (!depUri) { return null; } const dep = graph[depUri]; const component = getComponentMap(dep.content as DependencyNodeContent)[ parts.shift() || "default" ]; return [depUri, component]; } }; const maybeAddReference = ( stmt: ScriptExpression, _statements: [Reference, string][] = [] ) => { if (stmt.scriptKind === ScriptExpressionKind.Reference) { _statements.push([stmt, null]); } }; export const getMixins = (ast: Node): Record => { const styles = getStyleElements(ast); const mixins: Record = {}; for (const style of styles) { traverseSheet(style.sheet, style, (rule) => { if (rule && isRule(rule) && rule.ruleKind === RuleKind.Mixin) { mixins[rule.name.value] = rule; } }); } return mixins; }; export const getASTParentChildMap = memoize( (ast: Expression): Record => { const childParentMap = {}; traverseExpression(ast, null, (expr, owner) => { childParentMap[expr.id] = owner; }); return childParentMap; } ); export const getASTParent = (ast: Expression, root: Expression): Expression => { return getASTParentChildMap(root)[ast.id]; }; export const getASTAncestors = memoize( (ast: Expression, root: Expression): Expression[] => { let curr = ast; const map = getASTParentChildMap(root); const ancestors = []; while (curr) { curr = map[curr.id]; if (curr) { ancestors.push(curr); } } return ancestors; } ); export const isNode = (ast: Expression): ast is Node => NodeKind[(ast as Node).nodeKind] != null; export const isAttribute = (ast: Expression): ast is Attribute => AttributeKind[(ast as Attribute).attrKind] != null; export const isAttributeValue = (ast: Expression): ast is AttributeValue => AttributeValueKind[(ast as AttributeValue).attrValueKind] != null; export const isScriptExpression = (ast: Expression): ast is ScriptExpression => ScriptExpressionKind[(ast as ScriptExpression).scriptKind] != null; export const isDynamicStringAttributeValuePart = ( ast: Expression ): ast is DynamicStringAttributeValuePart => DynamicStringAttributeValuePartKind[ (ast as DynamicStringAttributeValuePart).partKind ] != null; export const traverseExpression = ( ast: Expression, owner: Expression, each: (node: Expression, parent: Expression) => void | boolean ) => { if (each(ast, owner) === false) { return false; } if (isNode(ast)) { switch (ast.nodeKind) { case NodeKind.Element: { return ( traverseExpressions(ast.attributes, ast, each) && traverseExpressions(ast.children, ast, each) ); } case NodeKind.Fragment: { return traverseExpressions(ast.children, ast, each); } case NodeKind.Slot: { return traverseJSExpression(ast.script, ast, each); } case NodeKind.StyleElement: { return traverseSheet(ast.sheet, ast, each); } } } else if (isAttribute(ast)) { if (ast.attrKind === AttributeKind.KeyValueAttribute && ast.value) { return traverseExpression(ast.value, ast, each); } } else if (isAttributeValue(ast)) { if (ast.attrValueKind === AttributeValueKind.Slot) { return traverseJSExpression(ast.script, ast, each); } } return true; }; const traverseExpressions = ( expressions: Expression[], owner: Expression, each: (node: Expression, parent: Expression) => void | boolean ) => { for (const child of expressions) { if (!traverseExpression(child, owner, each)) { return false; } } return true; }; export const getNestedReferences = ( node: Node, _statements: [Reference, string][] = [] ): [Reference, string][] => { if (node.nodeKind === NodeKind.Slot) { maybeAddReference(node.script, _statements); } else { if (node.nodeKind === NodeKind.Element) { for (const attr of node.attributes) { if ( attr.attrKind == AttributeKind.KeyValueAttribute && attr.value && attr.value.attrValueKind === AttributeValueKind.Slot ) { if (attr.value.script.scriptKind === ScriptExpressionKind.Node) { getNestedReferences(attr.value.script, _statements); } else if ( attr.value.script.scriptKind === ScriptExpressionKind.Reference ) { _statements.push([attr.value.script, attr.name]); } } else if ( attr.attrKind === AttributeKind.ShorthandAttribute && attr.reference.scriptKind === ScriptExpressionKind.Reference ) { _statements.push([attr.reference, attr.reference[0]]); } else if ( attr.attrKind === AttributeKind.SpreadAttribute && attr.script.scriptKind === ScriptExpressionKind.Reference ) { _statements.push([attr.script, attr.script[0]]); } } } for (const child of getChildren(node)) { if ( child.nodeKind === NodeKind.Element && hasAttribute(PREVIEW_ATTR_NAME, child) ) { continue; } getNestedReferences(child, _statements); } } return _statements; }; ================================================ FILE: packages/paperclip-utils/src/html/exports.ts ================================================ import { CSSExports } from "../css/exports"; export type PCExports = { components: Record; style: CSSExports; }; export type ComponentProperty = { name: string; optional: boolean; }; export type ComponentExport = { name: string; properties: Record; public: boolean; }; ================================================ FILE: packages/paperclip-utils/src/html/patcher.ts ================================================ import { Mutation, ActionKind } from "./virt-mtuation"; import { VirtualNode, VirtualElement, VirtualText, VirtualNodeKind, LoadedData, SheetInfo, VirtualFragment, EvaluatedDataKind, EvaluatedCSSData, DiffedDataKind, LoadedPCData, LoadedCSSData, EvaluatedPCData, } from "./virt"; import { DiffedEvent, EngineDelegateEvent, EngineDelegateEventKind, EvaluatedEvent, } from "../core/events"; import { patchCSSSheet } from "../css/patcher"; export const patchVirtNode = (root: VirtualNode, mutations: Mutation[]) => { for (const mutation of mutations) { let target = getVirtTarget(root, mutation.nodePath); const action = mutation.action; switch (action.kind) { case ActionKind.DeleteChild: { const element = target as VirtualElement; const children = element.children.concat(); children.splice(action.index, 1); target = { ...target, children } as VirtualElement; break; } case ActionKind.InsertChild: { const element = target as VirtualElement; const children = element.children.concat(); children.splice(action.index, 0, action.child); target = { ...target, children } as VirtualElement; break; } case ActionKind.ReplaceNode: { target = action.replacement; break; } case ActionKind.RemoveAttribute: { const element = target as VirtualElement; const attributes = { ...element.attributes }; delete attributes[action.name]; target = { ...target, attributes, } as VirtualElement; break; } case ActionKind.SetAttribute: { const element = target as VirtualElement; const attributes = { ...element.attributes }; if (!action.value) { attributes[action.name] = action.value; } else { attributes[action.name] = action.value; } target = { ...target, attributes, } as VirtualElement; break; } case ActionKind.SetElementSourceInfo: { target = { ...target, sourceInfo: action.value, } as VirtualElement; break; } case ActionKind.SetElementSourceId: { target = { ...target, sourceInfo: action.value, } as VirtualElement; break; } case ActionKind.SetAnnotations: { target = { ...target, annotations: action.value } as | VirtualElement | VirtualText; break; } case ActionKind.SetText: { target = { ...target, value: action.value } as VirtualText; break; } } root = updateNode(root, mutation.nodePath, target) as VirtualElement; } return root; }; export const getVirtTarget = ( mount: VirtualNode, nodePath: number[] ): VirtualNode => nodePath.reduce((current: VirtualElement | VirtualFragment, i) => { const c = current.children[i]; return c; }, mount); const updateNode = ( ancestor: VirtualNode, nodePath: number[], newNode: VirtualNode, depth = 0 ) => { if (depth === nodePath.length) { return newNode; } if ( ancestor.kind === VirtualNodeKind.Text || ancestor.kind === VirtualNodeKind.StyleElement || ancestor.kind === VirtualNodeKind.Slot ) { return newNode; } return { ...ancestor, children: [ ...ancestor.children.slice(0, nodePath[depth]), updateNode( ancestor.children[nodePath[depth]] as VirtualElement, nodePath, newNode, depth + 1 ), ...ancestor.children.slice(nodePath[depth] + 1), ], }; }; export const updateAllLoadedData = ( allData: Record, event: EngineDelegateEvent ): Record => { allData = updatePrimary(allData, event); // update dependents for (const name in allData) { const info = allData[name]; if (info.kind === EvaluatedDataKind.PC) { if (info.allImportedSheetUris.includes(event.uri)) { allData = { ...allData, [name]: { ...(allData[name] as EvaluatedPCData), importedSheets: getImportedSheets( allData, info.allImportedSheetUris ), }, }; } } } return allData; }; const updatePrimary = ( allData: Record, event: EngineDelegateEvent ): Record => { if (event.kind === EngineDelegateEventKind.Evaluated) { if (event.data.kind === EvaluatedDataKind.PC) { return { ...allData, [event.uri]: { ...event.data, importedSheets: getImportedSheets( allData, event.data.allImportedSheetUris ), }, }; } else { return { ...allData, [event.uri]: { ...event.data, }, }; } } else if (event.kind === EngineDelegateEventKind.Diffed) { const existingData = allData[event.uri]; // this will happen if client renderer loads data, but imported // resource has changed if (!existingData) { return allData; } if (event.data.kind === DiffedDataKind.PC) { const existingPCData = existingData as LoadedPCData; return { ...allData, [event.uri]: { ...existingPCData, exports: event.data.exports, importedSheets: getImportedSheets( allData, event.data.allImportedSheetUris ), allImportedSheetUris: event.data.allImportedSheetUris, dependencies: event.data.dependencies, sheet: patchCSSSheet(existingPCData.sheet, event.data.sheetMutations), preview: patchVirtNode(existingPCData.preview, event.data.mutations), }, }; } else { const existingCSSData = existingData as LoadedCSSData; return { ...allData, [event.uri]: { ...existingCSSData, exports: event.data.exports, sheet: patchCSSSheet(existingCSSData.sheet, event.data.mutations), }, }; } } return allData; }; const getImportedSheets = ( allData: Record, allImportedSheetUris: string[] ) => { // ick, wworks for now. const deps: SheetInfo[] = []; for (let i = 0, { length } = allImportedSheetUris; i < length; i++) { const depUri = allImportedSheetUris[i]; const data = allData[depUri]; if (data) { deps.push({ uri: depUri, index: i, sheet: data.sheet }); // scenario won't happen for renderer since renderers are only // concerned about the file that's currently opened -- ignore for now. Might } else { // console.error(`data not loaded, this shouldn't happen 😬.`); } } return deps; }; ================================================ FILE: packages/paperclip-utils/src/html/stringify-virt-node.ts ================================================ import { stringifyCSSSheet } from "../css/stringify-sheet"; import { VirtualNode } from "./virt"; import { Html5Entities } from "html-entities"; import { VirtualNodeKind } from ".."; const entities = new Html5Entities(); export const stringifyVirtualNode = ( node: VirtualNode, slotPlaceholder = "" ) => { switch (node.kind) { case VirtualNodeKind.Fragment: return stringifyChildren(node, slotPlaceholder); case VirtualNodeKind.Element: { let buffer = `<${node.tagName}`; for (const key in node.attributes) { const value = node.attributes[key]; if (value) { buffer += ` ${key}="${value}"`; } else { buffer += ` ${key}`; } } buffer += `>${stringifyChildren(node, slotPlaceholder)}`; return buffer; } case VirtualNodeKind.StyleElement: { return ``; } case VirtualNodeKind.Text: { return entities.decode(node.value); } case VirtualNodeKind.Slot: { return slotPlaceholder; } default: { throw new Error(`can't handle node`); } } }; const stringifyChildren = (node, slotPlaceholder) => node.children .map((child) => stringifyVirtualNode(child, slotPlaceholder)) .join(""); ================================================ FILE: packages/paperclip-utils/src/html/tree.ts ================================================ import { memoize } from "../core/memo"; import { nodePathToAry } from "./virt"; // core tree utils // eslint-disable-next-line export type BaseTreeNode = {}; export type BaseParentNode = { children: BaseTreeNode[]; } & BaseTreeNode; export const isNodeParent = (node: BaseTreeNode): node is BaseParentNode => (node as any).children != null; export const flattenTreeNode = memoize( (current: TNode): TNode[] => { const treeNodeMap = getTreeNodeMap(current); return Object.values(treeNodeMap); } ); export const getNodePath = memoize( (node: TNode, root: TNode) => { const map = getTreeNodeMap(root); for (const path in map) { const c = map[path]; if (c === node) return path; } } ); export const getNodeByPath = memoize( (nodePath: string, root: TNode) => { return getTreeNodeMap(root)[nodePath]; } ); export const getNodeAncestors = memoize( (nodePath: string, root: TNode) => { const pathAry = nodePathToAry(nodePath); const map = getTreeNodeMap(root); const ancestors = []; for (let i = pathAry.length; i--; ) { ancestors.push(getNodeByPath(pathAry.slice(0, i).join("."), root)); } return ancestors; } ); export const containsNode = ( node: TNode, root: TNode ) => getNodePath(node, root) != null; export const getTreeNodeMap = memoize( ( current: TNode, path = "" ): Record => { const map: Record = { [path]: current }; if (isNodeParent(current)) { Object.assign( map, ...current.children.map((child, i) => getTreeNodeMap(child, path ? path + "." + i : String(i)) ) ); } return map; } ); ================================================ FILE: packages/paperclip-utils/src/html/virt-mtuation.ts ================================================ import { ExprTextSource } from "../base/virt"; import { VirtScriptObject } from "../script/virt"; import { VirtualElementSourceInfo, VirtualNode } from "./virt"; export enum ActionKind { ReplaceNode = "ReplaceNode", InsertChild = "InsertChild", DeleteChild = "DeleteChild", SetAttribute = "SetAttribute", SetElementSourceInfo = "SetElementSourceInfo", SetAnnotations = "SetAnnotations", SourceUriChanged = "SourceUriChanged", SetText = "SetText", SetElementSourceId = "SetElementSourceId", RemoveAttribute = "RemoveAttribute", } export type BaseAction = { kind: TKind; }; export type ReplaceNode = { replacement: VirtualNode; } & BaseAction; export type InsertChild = { child: VirtualNode; index: number; } & BaseAction; export type DeleteChild = { index: number; } & BaseAction; export type SetAttribute = { name: string; value?: string; } & BaseAction; export type SetElementSourceInfo = { value: VirtualElementSourceInfo; } & BaseAction; export type SetElementSourceId = { value: string; } & BaseAction; export type SetAnnotations = { value?: VirtScriptObject; } & BaseAction; export type RemoveAttribute = { name: string; } & BaseAction; export type SetText = { value: string; } & BaseAction; export type Action = | ReplaceNode | InsertChild | DeleteChild | SetElementSourceInfo | SetAttribute | SetAnnotations | SetElementSourceId | SetText | RemoveAttribute; export type Mutation = { nodePath: number[]; action: Action; }; ================================================ FILE: packages/paperclip-utils/src/html/virt.ts ================================================ import { CSSExports } from "../css/exports"; import { PCExports } from "./exports"; import { DependencyContent } from "../core/graph"; import { VirtScriptObject } from "../script/virt"; import { memoize } from "../core/memo"; import { getNodeAncestors, getNodePath } from "./tree"; import { Mutation } from "./virt-mtuation"; import { CSSMutation } from "../css/virt-mutation"; import { flattenTreeNode } from ".."; export enum VirtualNodeKind { Element = "Element", Text = "Text", Slot = "Slot", Fragment = "Fragment", StyleElement = "StyleElement", } export type VirtNodeSource = { uri: string; path: number[]; }; export type FrameAnnotation = { width?: number; height?: number; x?: number; y?: number; title?: string; visible?: boolean; }; export type NodeAnnotations = { frame?: FrameAnnotation; tags?: string[]; visualRegresionTest?: boolean; }; export enum EvaluatedDataKind { PC = "PC", CSS = "CSS", } type BaseEvaluatedData = { kind: TKind; }; export type EvaluatedPCData = { allImportedSheetUris: string[]; dependencies: Record; sheet: any; preview: VirtualNode; // TODO - change to declarations, or something to imply declarations in the file. Definitions maybe? exports: PCExports; usedExprIds?: string[]; } & BaseEvaluatedData; export type EvaluatedCSSData = { sheet: any; // TODO - change to declarations, or something to imply declarations in the file. Definitions maybe? exports: CSSExports; } & BaseEvaluatedData; export type EvaluatedData = EvaluatedPCData | EvaluatedCSSData; export enum DiffedDataKind { CSS = "CSS", PC = "PC", } export type BaseDiffedData = { kind: TKind; }; export type DiffedCSSData = { exports: CSSExports; mutations: CSSMutation[]; } & BaseDiffedData; export type DiffedPCData = { allImportedSheetUris: string[]; dependencies: Record; sheetMutations: CSSMutation[]; mutations: Mutation[]; exports: PCExports; } & BaseDiffedData; export type DiffedData = DiffedCSSData | DiffedPCData; export type SheetInfo = { sheet: any; index: number; uri: string; }; export type LoadedPCData = { importedSheets: SheetInfo[]; } & EvaluatedPCData; export type LoadedCSSData = EvaluatedCSSData; export type LoadedData = LoadedPCData | LoadedCSSData; type VirtualBaseNode = { sourceId: string; id: string; kind: KKind; }; export type VirtualStyleElement = { sheet: any; } & VirtualBaseNode; export type VirtualElementInstanceOfInfo = { componentName: string; }; export type VirtualElementSourceInfo = { instanceOf?: VirtualElementInstanceOfInfo; }; export type VirtualElement = { annotations?: VirtScriptObject; tagName: string; sourceInfo?: VirtualElementSourceInfo; attributes: { [identifier: string]: string | null; }; children: VirtualNode[]; } & VirtualBaseNode; export type VirtualText = { annotations?: VirtScriptObject; value: string; } & VirtualBaseNode; export type VirtualSlot = VirtualBaseNode; export type VirtualFragment = { children: VirtualNode[]; } & VirtualBaseNode; export type VirtualNode = | VirtualElement | VirtualText | VirtualSlot | VirtualFragment | VirtualStyleElement; export type VirtualFrame = VirtualElement | VirtualText; export const getStyleExports = (data: LoadedData) => data.kind === EvaluatedDataKind.PC ? data.exports.style : data.exports; export const nodePathToAry = memoize((path: string) => path.split(".").map(Number) ); export const getElementLabel = (node: VirtualElement) => node.attributes["data-pc-label"] || node.sourceInfo?.instanceOf?.componentName; export const isInstance = (node: VirtualNode) => node.kind === VirtualNodeKind.Element && Boolean(node.sourceInfo?.instanceOf); export const getInstanceAncestor = (node: VirtualNode, root: VirtualNode) => getNodeAncestors(getNodePath(node, root), root).find(isInstance); export const getVirtNodeIdMap = memoize( (node: VirtualNode): Record => { const children = flattenTreeNode(node) as VirtualNode[]; const map = {}; for (const child of children) { map[child.id] = child; } return map; } ); export const getVirtNodeBySourceId = (node: VirtualNode, sourceId: string) => { const map = getVirtNodeIdMap(node); for (const virtId in map) { if (map[virtId].sourceId === sourceId) { return map[virtId]; } } return null; }; // export const createVirtNodeSource = (path: number[], uri: string): VirtNodeSource => ({ // uri, // path // }); // export const createVirtNodeSourceFromInstance = (instance: VirtualNode, rendered: Record) => { // for (const uri in rendered) { // const data = rendered[uri]; // if (data.kind === EvaluatedDataKind.PC && containsNode(instance, data.preview)) { // return createVirtNodeSource(getNodePath(instance, data.preview).split(".").map(Number), uri); // } // } // }; ================================================ FILE: packages/paperclip-utils/src/index.ts ================================================ export * from "./core/events"; export * from "./html/virt"; export * from "./html/ast"; export * from "./script/ast"; export * from "./core/actions"; export * from "./script/virt"; export * from "./css/stringify-sheet"; export * from "./css/ast"; export * from "./base/ast"; export * from "./core/config"; export * from "./core/constants"; // export * from "./errors"; export * from "./core/graph"; export * from "./html/virt-mtuation"; export * from "./core/resolve"; export * from "./html/stringify-virt-node"; export * from "./css/virt"; export * from "./html/patcher"; export * from "./core/utils"; export * from "./css/patcher"; export * from "./core/memo"; export * from "./base/virt"; export * from "./core/errors"; export * from "./core/diagnostics"; export * from "./html/tree"; export * from "./css/style-inspection"; export * from "./core/module-ast"; export * from "./css/exports"; export * from "./html/exports"; export * from "./core/report"; ================================================ FILE: packages/paperclip-utils/src/node/source-watcher.ts ================================================ import * as chokidar from "chokidar"; import * as path from "path"; import * as url from "url"; import { EventEmitter } from "events"; import { paperclipSourceGlobPattern } from "../core/utils"; import { isPaperclipResourceFile } from ".."; export enum ChangeKind { Removed, Added, Changed, } const CHOKIDAR_EVENT_MAP = { add: ChangeKind.Added, unlink: ChangeKind.Removed, change: ChangeKind.Changed, }; export class PaperclipResourceWatcher { private _em: EventEmitter; private _watcher: chokidar.FSWatcher; constructor(private _srcDir: string, readonly cwd: string) { this._em = new EventEmitter(); this._init(); } onChange(listener: (changeKind: ChangeKind, filePath: string) => void) { this._em.on("change", listener); return () => this._em.off("change", listener); } dispose() { this._watcher.close(); } private _init() { const watcher = (this._watcher = chokidar.watch( paperclipSourceGlobPattern(this._srcDir), { cwd: this.cwd, ignoreInitial: true, ignored: "**/node_modules/**", followSymlinks: true, } )); watcher.on("all", (eventName, relativePath) => { if (!isPaperclipResourceFile(relativePath)) { return; } const filePath = relativePath.charAt(0) !== "/" ? path.join(this.cwd, relativePath) : relativePath; const changeKind = CHOKIDAR_EVENT_MAP[eventName]; if (changeKind) { this._em.emit("change", changeKind, url.pathToFileURL(filePath).href); } }); } } ================================================ FILE: packages/paperclip-utils/src/script/ast.ts ================================================ import { Node, traverseExpression, Expression } from "../html/ast"; import { StringRange } from "../base/ast"; export enum ScriptExpressionKind { Node = "Node", Reference = "Reference", Array = "Array", Object = "Object", String = "String", Number = "Number", Boolean = "Boolean", Conjunction = "Conjunction", Not = "Not", Group = "Group", } type BaseScriptExpression = { id: string; scriptKind: TKind; }; export type ScriptNode = Node & BaseScriptExpression; export type ScriptObjectProperty = { key: string; value: ScriptExpression; }; export enum ScriptConjunctionOperatorKind { And = "And", Or = "Or", } export type ScriptConjunction = { left: ScriptExpression; range: StringRange; right: ScriptExpression; operator: ScriptConjunctionOperatorKind; } & BaseScriptExpression; export type ScriptObject = { properties: ScriptObjectProperty[]; range: StringRange; } & BaseScriptExpression; export type ScriptArray = { values: ScriptExpression[]; range: StringRange; } & BaseScriptExpression; export type ScriptString = { value: string; range: StringRange; } & BaseScriptExpression; export type ScriptBoolean = { value: boolean; range: StringRange; } & BaseScriptExpression; export type ScriptNot = { expression: ScriptExpression; range: StringRange; } & BaseScriptExpression; export type ScriptGroup = { expression: ScriptExpression; range: StringRange; } & BaseScriptExpression; export type ScriptNumber = { value: number; range: StringRange; } & BaseScriptExpression; export type Reference = { path: ReferencePart[]; range: StringRange; } & BaseScriptExpression; export type ReferencePart = { name: string; optional: boolean; }; export type ScriptExpression = | Reference | ScriptNode | ScriptObject | ScriptGroup | ScriptArray | ScriptNumber | ScriptString | ScriptBoolean | ScriptConjunction | ScriptNot; export const traverseJSExpression = ( expr: ScriptExpression, owner: Expression, each: (expr: ScriptExpression, parent: Expression) => void | boolean ) => { if (expr.scriptKind === ScriptExpressionKind.Node) { return traverseExpression(expr, owner, each); } if (each(expr, owner) === false) { return false; } if (expr.scriptKind === ScriptExpressionKind.Conjunction) { return ( traverseJSExpression(expr.left, expr, each) && traverseJSExpression(expr.right, expr, each) ); } else if (expr.scriptKind === ScriptExpressionKind.Array) { for (const value of expr.values) { if (traverseJSExpression(value, expr, each) === false) { return false; } } } return true; }; ================================================ FILE: packages/paperclip-utils/src/script/virt.ts ================================================ import { ExprTextSource } from "../base/virt"; import { memoize } from "../core/memo"; export enum VirtScriptObjectKind { Object = "Object", Array = "Array", Boolean = "Boolean", Number = "Number", Str = "Str", } type BaseVirtScriptObject = { kind: TKind; }; export type VirtScriptObject = { values: Record; sourceId: string; } & BaseVirtScriptObject; export type VirtScriptArray = { values: VirtJsValue[]; } & BaseVirtScriptObject; export type VirtScriptNumber = { value: number; } & BaseVirtScriptObject; export type VirtScriptBoolean = { value: boolean; } & BaseVirtScriptObject; export type VirtScriptString = { value: string; } & BaseVirtScriptObject; export type VirtJsValue = | VirtScriptObject | VirtScriptArray | VirtScriptNumber | VirtScriptBoolean | VirtScriptString; export const computeVirtScriptObject = memoize((obj: VirtScriptObject) => { const values = {}; for (const key in obj.values) { values[key] = computeVirtScriptValue(obj.values[key]); } return values; }); export const toVirtScriptValue = memoize((value: any) => { if (Array.isArray(value)) { return { kind: VirtScriptObjectKind.Array, values: value.map(toVirtScriptValue), }; } else if (value && typeof value === "object") { const values = {}; for (const k in value) { values[k] = toVirtScriptValue(value[k]); } return { kind: VirtScriptObjectKind.Object, values, }; } else if (typeof value === "number") { return { kind: VirtScriptObjectKind.Number, value, }; } else if (typeof value === "string") { return { kind: VirtScriptObjectKind.Str, value, }; } else if (typeof value === "boolean") { return { kind: VirtScriptObjectKind.Boolean, value, }; } }); export const computeVirtScriptValue = memoize((obj: VirtJsValue) => { switch (obj.kind) { case VirtScriptObjectKind.Object: { return computeVirtScriptObject(obj); } case VirtScriptObjectKind.Array: { return obj.values.map(computeVirtScriptValue); } case VirtScriptObjectKind.Str: case VirtScriptObjectKind.Boolean: case VirtScriptObjectKind.Number: { return obj.value; } } }); ================================================ FILE: packages/paperclip-utils/src/test/basic-test.ts ================================================ ================================================ FILE: packages/paperclip-utils/src/test/config-test.ts ================================================ import { PaperclipConfig, buildCompilerOptions } from "../core/config"; import { expect } from "chai"; describe(__filename + "#", () => { describe("Compiler options", () => { it(`can be built from non-array config`, () => { const options = buildCompilerOptions({ srcDir: "./src", compilerOptions: { outDir: "a" } }); expect(options).to.eql([{ outDir: "a" }]); }); it(`can be built from an array config`, () => { const options = buildCompilerOptions({ srcDir: "./src", compilerOptions: [ { outDir: "a" }, { outDir: "b" } ] }); expect(options).to.eql([{ outDir: "a" }, { outDir: "b" }]); }); it(`can be built from an array `, () => { const options = buildCompilerOptions({ srcDir: "./src", compilerOptions: [ { outDir: "b" }, { outDir: "c" } ] }); expect(options).to.eql([{ outDir: "b" }, { outDir: "c" }]); }); it(`generates compiler options anyways if not specified `, () => { const options = buildCompilerOptions({ srcDir: "./src" }); expect(options).to.eql([{ outDir: "./src" }]); }); it(`sets outdir as srcDir is not specified`, () => { const options = buildCompilerOptions({ srcDir: "./src", compilerOptions: {} }); expect(options).to.eql([{ outDir: "./src" }]); }); }); }); ================================================ FILE: packages/paperclip-utils/tsconfig.json ================================================ { "compilerOptions": { "outDir": "lib", "target": "es5", "declaration": true, "moduleResolution": "node", "module": "commonjs", "allowSyntheticDefaultImports": true }, "exclude": ["node_modules", "*-test", "lib", "index.d.ts", "test"], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-vscode/.eslintrc.json ================================================ { "root": true, "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], "rules": { "no-constant-condition": 0 } } ================================================ FILE: packages/paperclip-vscode/.gitignore ================================================ .vscode-test paperclip-html.tmLanguage.json ================================================ FILE: packages/paperclip-vscode/.npmignore ================================================ *.vsix .vscode-test src ================================================ FILE: packages/paperclip-vscode/.nycrc.json ================================================ { "exclude": [".vscode-test*", "*node_modules*"] } ================================================ FILE: packages/paperclip-vscode/.vscodeignore ================================================ ================================================ FILE: packages/paperclip-vscode/LICENSE ================================================ Copyright (C) 2021 Craig Condon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. ================================================ FILE: packages/paperclip-vscode/README.md ================================================ **Paperclip is a UI language centered around increasing the speed, safety, and accuracy of developing web interfaces**. This extension allows you to build them in realtime. Documentation for this extension can be viewed [here](https://paperclip.dev/docs/getting-started-vscode). ![VSCode Demo](https://user-images.githubusercontent.com/757408/75412579-f0965200-58f0-11ea-8043-76a0b0ec1a08.gif) #### Extension Features - `Paperclip: Open Live Preview` command for opening Paperclip previews. - CSS color picker in Paperclip files. - `command + click` preview elements to reveal their source. - `command + click` reference in documents to reveal source. ================================================ FILE: packages/paperclip-vscode/languages/paperclip-css-language-configuration.json ================================================ { "comments": { "blockComment": ["/*", "*/"] }, "brackets": [["{", "}"]], "autoClosingPairs": [ ["{", "}"], ["@include", ";"] ], "surroundingPairs": [[":", ";"]] } ================================================ FILE: packages/paperclip-vscode/languages/paperclip-html-language-configuration.json ================================================ { "comments": { "blockComment": [""] }, "brackets": [ ["{#", "{/"], ["{", "}"], ["[", "]"], ["(", ")"], [""], ["<", ">"] ], "autoClosingPairs": [ ["{", "}"], ["[", "]"], ["(", ")"], ["\"", "\""], ["'", "'"] ], "surroundingPairs": [ ["{", "}"], ["[", "]"], ["(", ")"], ["\"", "\""], ["'", "'"], ["<", ">"] ] } ================================================ FILE: packages/paperclip-vscode/package.json ================================================ { "name": "paperclip-vscode", "displayName": "Paperclip", "version": "18.5.6", "electronVersion": "7.1.10", "publisher": "crcn", "preview": true, "private": true, "publishConfig": { "access": "public" }, "keywords": [ "React", "CSS", "HTML", "visual", "preview", "realtime", "live", "template" ], "description": "IDE extension for Paperclip", "homepage": "http://paperclip.dev", "icon": "assets/logo.png", "repository": { "type": "git", "url": "https://github.com/paperclipui/paperclip.git" }, "main": "lib/extension/index.js", "engines": { "vscode": "^1.41.0" }, "activationEvents": [ "onLanguage:paperclip", "onWebviewPanel:paperclip-preview" ], "categories": [ "Programming Languages" ], "contributes": { "configuration": { "title": "Paperclip", "properties": { "credentials.browserstackUsername": { "type": "string" }, "credentials.browserstackPassword": { "type": "string" } } }, "commands": [ { "command": "paperclip.openPreview", "title": "Paperclip: Open Live Preview" }, { "command": "paperclip.openStickyPreview", "title": "Paperclip: Open Sticky Preview" }, { "command": "paperclip.giveFeedback", "title": "Paperclip: Give Feedback" } ], "menus": { "commandPalette": [ { "command": "paperclip.openPreview", "when": "editorLangId == paperclip" }, { "command": "paperclip.openStickyPreview", "when": "editorLangId == paperclip" } ] }, "keybindings": [ { "command": "paperclip.openPreview", "key": "shift+cmd+v", "when": "editorLangId == paperclip" }, { "command": "paperclip.openStickyPreview", "key": "shift+cmd+b", "when": "editorLangId == paperclip" } ], "languages": [ { "id": "paperclip", "aliases": [ "paperclip", "paperclip" ], "extensions": [ ".pc" ], "configuration": "./languages/paperclip-html-language-configuration.json" }, { "id": "pcss", "aliases": [ "paperclip-css", "paperclip-css" ], "extensions": [ ".pcss" ], "configuration": "./languages/paperclip-css-language-configuration.json" } ], "grammars": [ { "language": "paperclip", "scopeName": "text.html.paperclip-html", "path": "./syntaxes/paperclip-html.tmLanguage.json", "embeddedLanguages": { "source.pcss": "pcss", "source.js": "javascript" } }, { "language": "pcss", "scopeName": "source.pcss", "path": "./syntaxes/paperclip-css.json" } ] }, "scripts": { "build": "tsc && npm run build:syntaxes", "build:syntaxes": "node lib/scripts/build", "build:syntaxes:watch": "nodemon --exec \"node lib/scripts/build\" --watch ./syntaxes/paperclip-html.yaml", "xtest": "node lib/test/run", "lint": "eslint src", "build:watch": "concurrently \"tsc --watch --preserveWatchOutput\" \"npm run build:syntaxes:watch\"", "prepublishOnly": "npm run build", "rm-ngrok": "rm -rf node_modules/ngrok/bin", "publish:local": "rm -rf node_modules && npm install && npm run rm-ngrok && vsce package && vsce publish", "publish:marketplace": "rm -rf node_modules && npm install && npm run rm-ngrok && vsce package && vsce publish -p $VSCE_TOKEN" }, "author": "", "license": "MIT", "devDependencies": { "@types/lodash": "^4.14.157", "@types/mocha": "^7.0.2", "@types/node": "^13.1.8", "@types/vscode": "^1.41.0", "@typescript-eslint/eslint-plugin": "^3.6.1", "@typescript-eslint/parser": "^3.6.1", "chai": "^4.2.0", "concurrently": "^5.3.0", "eslint": "^7.4.0", "js-yaml": "^4.0.0", "mocha": "^8.0.1", "nodemon": "^2.0.7", "plist": "^3.0.1", "typescript": "^4.2.3", "vscode-test": "^1.4.0", "vscode-textmate": "^5.2.0" }, "dependencies": { "@paperclip-ui/autocomplete": "^18.5.6", "@paperclip-ui/common": "^18.5.3", "@paperclip-ui/core": "^18.5.6", "@paperclip-ui/language-service": "^18.5.6", "@paperclip-ui/source-writer": "^18.5.6", "@paperclip-ui/utils": "^18.5.6", "@paperclip-ui/web-renderer": "^18.5.6", "@tandem-ui/designer": "^18.5.6", "@tandem-ui/workspace": "^18.5.6", "axios": "^0.21.1", "browserstack": "^1.6.1", "browserstack-local": "^1.4.8", "cheerio": "^1.0.0-rc.3", "color": "^3.1.2", "find-up": "^4.1.0", "glob": "^7.1.6", "lodash": "^4.17.15", "mousetrap": "^1.6.5", "p-limit": "^3.0.2", "react": "^17.0.2", "react-dom": "^17.0.2", "vscode-languageclient": "6.0.1", "vscode-languageserver": "6.0.1", "vscode-languageserver-protocol": "3.15.3", "vscode-languageserver-textdocument": "^1.0.0", "ws": "^8.4.2" }, "gitHead": "28439bfc22f88290b1a12e4f364bdd19667d715a" } ================================================ FILE: packages/paperclip-vscode/paperclip/.vscodeignore ================================================ .vscode/** .vscode-test/** .gitignore vsc-extension-quickstart.md ================================================ FILE: packages/paperclip-vscode/paperclip/CHANGELOG.md ================================================ ## 18.0.0 - deprecate `className` in favor of just `class` - interim compiler to make it easier to create target compilers - include `line` + `column` in AST positions - add ability to generate source maps ================================================ FILE: packages/paperclip-vscode/paperclip/README.md ================================================ # paperclip README This is the README for your extension "paperclip". After writing up a brief description, we recommend including the following sections. ## Features Describe specific features of your extension including screenshots of your extension in action. Image paths are relative to this README file. For example if there is an image subfolder under your extension project workspace: \!\[feature X\]\(images/feature-x.png\) > Tip: Many popular extensions utilize animations. This is an excellent way to show off your extension! We recommend short, focused animations that are easy to follow. ## Requirements If you have any requirements or dependencies, add a section describing those and how to install and configure them. ## Extension Settings Include if your extension adds any VS Code settings through the `contributes.configuration` extension point. For example: This extension contributes the following settings: - `myExtension.enable`: enable/disable this extension - `myExtension.thing`: set to `blah` to do something ## Known Issues Calling out known issues can help limit users opening duplicate issues against your extension. ## Release Notes Users appreciate release notes as you update your extension. ### 1.0.0 Initial release of ... ### 1.0.1 Fixed issue #. ### 1.1.0 Added features X, Y, and Z. --- ## Working with Markdown **Note:** You can author your README using Visual Studio Code. Here are some useful editor keyboard shortcuts: - Split the editor (`Cmd+\` on macOS or `Ctrl+\` on Windows and Linux) - Toggle preview (`Shift+CMD+V` on macOS or `Shift+Ctrl+V` on Windows and Linux) - Press `Ctrl+Space` (Windows, Linux) or `Cmd+Space` (macOS) to see a list of Markdown snippets ### For more information - [Visual Studio Code's Markdown Support](http://code.visualstudio.com/docs/languages/markdown) - [Markdown Syntax Reference](https://help.github.com/articles/markdown-basics/) **Enjoy!** ================================================ FILE: packages/paperclip-vscode/paperclip/language-configuration.json ================================================ { "comments": { // symbol used for single line comment. Remove this entry if your language does not support line comments "lineComment": "//", // symbols used for start and end a block comment. Remove this entry if your language does not support block comments "blockComment": ["/*", "*/"] }, // symbols used as brackets "brackets": [ ["{", "}"], ["[", "]"], ["(", ")"] ], // symbols that are auto closed when typing "autoClosingPairs": [ ["{", "}"], ["[", "]"], ["(", ")"], ["\"", "\""], ["'", "'"] ], // symbols that can be used to surround a selection "surroundingPairs": [ ["{", "}"], ["[", "]"], ["(", ")"], ["\"", "\""], ["'", "'"] ] } ================================================ FILE: packages/paperclip-vscode/paperclip/package.json ================================================ { "name": "paperclip", "displayName": "paperclip", "description": "Template language that compiles to code", "version": "0.0.1", "engines": { "vscode": "^1.41.0" }, "categories": [ "Programming Languages" ], "contributes": { "languages": [ { "id": "paperclip", "aliases": [ "Paperclip", "paperclip" ], "extensions": [ ".pc" ], "configuration": "./language-configuration.json" } ], "grammars": [ { "language": "paperclip", "scopeName": "source.paperclip", "path": "./syntaxes/paperclip.tmLanguage.json" } ] } } ================================================ FILE: packages/paperclip-vscode/paperclip/syntaxes/paperclip.tmLanguage.json ================================================ { "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", "name": "Paperclip", "patterns": [ { "include": "#keywords" }, { "include": "#strings" } ], "repository": { "keywords": { "patterns": [ { "name": "keyword.control.paperclip", "match": "\\b(if|while|for|return)\\b" } ] }, "strings": { "name": "string.quoted.double.paperclip", "begin": "\"", "end": "\"", "patterns": [ { "name": "constant.character.escape.paperclip", "match": "\\\\." } ] } }, "scopeName": "source.paperclip" } ================================================ FILE: packages/paperclip-vscode/scripts/scrape-css-declarations.js ================================================ // TODO - update this to include existing declaration // values so that we don't have to redo it again. const { scrape } = require("./utils"); const { uniq, flatten } = require("lodash"); const plimit = require("p-limit"); const { snakeCase } = require("lodash"); /* Scrapes for all CSS declarations & values */ const CSS_REF_URL = "https://www.w3schools.com/cssref/"; async function getCSSProperties() { return scrape("https://www.w3schools.com/cssref/", $ => { return $("#cssproperties td > a") .map(function() { return [[$(this).text(), $(this).attr("href")]]; }) .get(); }); } async function getCSSPropertyValue(link) { return scrape("https://www.w3schools.com/cssref/" + link, $ => { return $(".w3-table-all.notranslate tr > td:first-child") .map(function() { return [ [ $(this) .text() .trim(), $(this).find("a").length > 0 ] ]; }) .get(); }); } async function getCSSPropertyValues() { const declarations = []; const props = await getCSSProperties(); const limit = plimit(30); const usedNames = {}; await Promise.all( props.map(([name, link]) => limit(async () => { if (usedNames[name]) { return; } usedNames[name] = true; const warnManual = () => { console.warn(`⚠️ "${name}" will have to be manually added`); }; if (name.charAt(0) === "@") { return warnManual(); } console.info(`🧲 scraping "${name}"`); try { let properties = await getCSSPropertyValue(link); properties = properties.filter(([propertyName]) => { if ( /^(length|color|url|number|span n|time|keyframename|percentage|\w-offset|\w+-radius|\w-shadow)$|%|xpos|ypos|-axis|max-content|min-content\//.test( propertyName ) ) { console.log(`◻️ skipping "${name}: ${propertyName}"`); return false; } if (/\(.*?\)/.test(propertyName)) { console.warn( `⚠️ "${name}: ${propertyName};" will have to be manually added` ); return false; } return true; }); const used = {}; properties = properties .map(([prop, ref]) => { return [prop.replace(/[\s\n]+/g, " ").split(" "), ref]; }) .reduce((flattened, [props, ref]) => { return [...flattened, ...props.map(prop => [prop, ref])]; }, []) .filter(prop => { if (used[prop[0]]) { return false; } used[prop[0]] = 1; return prop; }); declarations.push({ name, properties }); } catch (e) { warnManual(); } }) ) ); return declarations.sort((a, b) => { for (const [prop, ref] of a.properties) { if (ref && declarations.some(decl => decl.name === prop)) { return 1; } } return -1; }); } function getValuesKey(name) { return `${snakeCase(name).toUpperCase()}_VALUES`; } function translateDeclarations(declarations) { const buffer = [`const {uniq} = require("lodash");\n\n`]; for (const { name, properties } of declarations) { buffer.push(`const ${getValuesKey(name)} = uniq([\n`); for (const [valueName, ref] of properties) { if (ref) { if (declarations.some(decl => decl.name === valueName)) { buffer.push(` ...${getValuesKey(valueName)},\n`); } else { console.warn( `⚠️ "${name}: ${valueName}" will have to be manually added.` ); } } else { buffer.push(` "${valueName}",\n`); } } buffer.push(`]);\n\n`); } buffer.push("export const CSS_DECLARATION_VALUE_MAP = {\n"); for (const { name } of declarations) { buffer.push(` "${name}": ${getValuesKey(name)},\n`); } buffer.push("};\n"); return buffer.join(""); } async function run() { const record = await getCSSPropertyValues(); const code = translateDeclarations(record); console.log("\n\n\n"); console.log(code); } run(); ================================================ FILE: packages/paperclip-vscode/scripts/utils.js ================================================ const axios = require("axios"); const cheerio = require("cheerio"); class Scraper {} exports.scrape = async (url, scrapper) => { const resp = await axios.get(url); const $ = cheerio.load(resp.data); return scrapper($); }; ================================================ FILE: packages/paperclip-vscode/src/extension/channels.ts ================================================ import { remoteChannel } from "@paperclip-ui/common"; import { ExprSource } from "@paperclip-ui/utils"; export type DesignServerStartedInfo = { httpPort: number; projectId: string; }; export const designServerStartedChannel = remoteChannel< DesignServerStartedInfo, void >("designServerStartedChannel"); export const revealSourceChannel = remoteChannel( "revealSourceChannel" ); ================================================ FILE: packages/paperclip-vscode/src/extension/command-manager.ts ================================================ import { commands } from "vscode"; import { LiveWindowManager } from "./preview/live-window-manager"; import { Uri, window, env } from "vscode"; import { isPaperclipFile } from "@paperclip-ui/utils"; import { fixFileUrlCasing } from "./utils"; export class CommandManager { constructor(private _windows: LiveWindowManager) {} private _openPreview(sticky: boolean) { if (window.activeTextEditor) { if (isPaperclipFile(String(window.activeTextEditor.document.uri))) { const paperclipUri = fixFileUrlCasing( String(window.activeTextEditor.document.uri) ); this._windows.open(paperclipUri, sticky); } else { window.showErrorMessage( `Only Paperclip (.pc) are supported in Live Preview` ); } } } activate() { commands.registerCommand("paperclip.openPreview", () => this._openPreview(false) ); commands.registerCommand("paperclip.openStickyPreview", () => this._openPreview(true) ); commands.registerCommand("paperclip.giveFeedback", () => { env.openExternal( Uri.parse("https://github.com/paperclipui/paperclip/issues") ); }); } } ================================================ FILE: packages/paperclip-vscode/src/extension/document-manager.ts ================================================ import { LiveWindowManager } from "./preview/live-window-manager"; import { Selection, TextEdit, TextEditor, Range, TextDocumentChangeEvent, ViewColumn, window, workspace, TextDocument, WorkspaceEdit, Uri, TextDocumentChangeReason, } from "vscode"; import { fixFileUrlCasing } from "./utils"; import * as fs from "fs"; import * as URL from "url"; import { ExprSource, isPaperclipResourceFile, stripFileProtocol, } from "@paperclip-ui/utils"; import { PaperclipLanguageClient } from "./language"; import { DesignServerStartedInfo } from "./channels"; import * as pce from "@paperclip-ui/editor-engine/lib/core/crdt-document"; import { EditorClient } from "@paperclip-ui/editor-engine/lib/client/client"; import { wsAdapter } from "@paperclip-ui/common"; import * as ws from "ws"; enum OpenLivePreviewOptions { Yes = "Yes", Always = "Always", No = "No", } // TODO: need to simplify this class a bit, it's doing too many things export class DocumentManager { private _showedOpenLivePreviewPrompt: boolean; private _editorClient: EditorClient; private _remoteDocs: Record = {}; constructor( private _windows: LiveWindowManager, private _client: PaperclipLanguageClient ) { console.log("DocumentManager::constructor"); this._client.onRevealSourceRequest(this._onRevealSourceRequested); this._client.onDesignServerStarted(this._onDesignServerStarted); workspace.onDidChangeTextDocument(this._onTextDocumentChange); } activate() { window.onDidChangeActiveTextEditor(this._onActiveTextEditorChange); if (window.activeTextEditor) { this._onActiveTextEditorChange(window.activeTextEditor); } } private _onActiveTextEditorChange = (editor: TextEditor) => { if (!editor) { return; } const uri = fixFileUrlCasing(String(editor.document.uri)); if ( !this._windows.setStickyWindowUri(uri) && this._windows.getLength() === 0 ) { this._askToDisplayLivePreview(uri); } }; private _askToDisplayLivePreview = async (uri: string) => { if (this._showedOpenLivePreviewPrompt) { return false; } this._showedOpenLivePreviewPrompt = true; const option = await window.showInformationMessage( `Would you like to open a live preview? Command: "Paperclip: Open Live Preview" is also available. `, OpenLivePreviewOptions.Always, OpenLivePreviewOptions.Yes, OpenLivePreviewOptions.No ); if (option === OpenLivePreviewOptions.Yes) { this._windows.open(uri, false); } else if (option === OpenLivePreviewOptions.Always) { this._windows.open(uri, true); } }; private _onDidOpenTextDocument = async (e: TextDocument) => { const uri = e.uri.toString(); if (!isPaperclipResourceFile(uri) || this._remoteDocs[uri]) { return; } const source = (this._remoteDocs[uri] = await this._editorClient .getDocuments() .open(uri) .then((doc) => doc.getSource())); // need to replace design server text completely since VS Code // may store unsaved changes source.setText(e.getText().split(""), 0, source.getText().length); source.onSync(() => { // don't bother syncing if the docs are identical if (source.getText() === e.getText()) { return; } console.log(`Replacing text content`); // If not identical, then patch text editor doc to match CRDT doc since that is // the source of truth const selection = window.activeTextEditor?.selection; // !! We're replacing the entire text document whenever the CRDT changes and doesn't match _this_ text document // !! Previously we'd apply patches from CRDT docs, but this quickly becomes out of sync (especially on FS). The // !! Simple answer _for now_ is to replace the _entire_ text document so that we're certain about syncing. const edit = new WorkspaceEdit(); edit.set(Uri.parse(uri), [ new TextEdit( new Range(e.positionAt(0), e.positionAt(e.getText().length)), source.getText() ), ]); workspace.applyEdit(edit); if (selection) { window.activeTextEditor.selection = selection; } }); }; private _onTextDocumentChange = async (event: TextDocumentChangeEvent) => { const uri = event.document.uri.toString(); // check if saved locally, and ignore if that happened since PC worker // will already receive local file changes. We need to ensure that we don't accidentally // send contentChanges which will append information to the doc that causes foo-y stuff // to happen if (!isPaperclipResourceFile(uri)) { return; } const source = this._remoteDocs[uri]; // if (fs.readFileSync(URL.fileURLToPath(uri), "utf-8") === event.document.getText() && event.document.getText() === source. // This will happen on sync, so make sure we're not executing OTs on a doc // where the transforms originally came from if ( event.document.getText() === source.getText() || event.contentChanges.length === 0 || // A bit hacky, but we want to make sure to ignore // when changes are coming from VSCode's built-in file watcher, since these // changes will be caught by the Paperclip workspace. // !! This code _BREAKS_ when a new file is added, and changed. I think this is a bug with VS Code (event.reason !== TextDocumentChangeReason.Redo && event.reason !== TextDocumentChangeReason.Undo && !event.document.isDirty) ) { console.log("ignore change"); return; } const edits: pce.TextEdit[] = event.contentChanges.map((change) => { return { chars: change.text.split(""), index: change.rangeOffset, deleteCount: change.rangeLength, }; }); const now = Date.now(); source.applyEdits(edits); console.log( "DocumentManager::_onTextDocumentChange in %d ms", Date.now() - now ); }; private _onDesignServerStarted = (info: DesignServerStartedInfo) => { this._editorClient = new EditorClient( wsAdapter(() => new ws.WebSocket(`ws://localhost:${info.httpPort}/ws`)) ); workspace.onDidOpenTextDocument(this._onDidOpenTextDocument); workspace.textDocuments.forEach(this._onDidOpenTextDocument); }; private _onRevealSourceRequested = async ({ textSource }: ExprSource) => { console.log("On Reveal Source request"); // shouldn't happen, but might if text isn't loaded if (!textSource) { return; } const textDocument = await this._openDoc(textSource.uri); const editor: TextEditor = window.visibleTextEditors.find( (editor) => editor.document && fixFileUrlCasing(String(editor.document.uri)) === fixFileUrlCasing(textSource.uri) ) || (await window.showTextDocument(textDocument, ViewColumn.One)); editor.selection = new Selection( textDocument.positionAt(textSource.range.start.pos), textDocument.positionAt(textSource.range.end.pos) ); editor.revealRange(editor.selection); }; private _openDoc = async (uri: string) => { return ( workspace.textDocuments.find((doc) => String(doc.uri) === uri) || (await workspace.openTextDocument(stripFileProtocol(uri))) ); }; } ================================================ FILE: packages/paperclip-vscode/src/extension/index.ts ================================================ import { ExtensionContext } from "vscode"; import { LanguageClient } from "vscode-languageclient"; import { Disposable, Observable } from "@paperclip-ui/common"; import { PaperclipLanguageClient } from "./language"; import { LiveWindowManager } from "./preview/live-window-manager"; import { CommandManager } from "./command-manager"; import { DocumentManager } from "./document-manager"; class PaperclipExtension implements Disposable { /** * Manages PC language features + the server */ private _languageClient: PaperclipLanguageClient; /** * Manages live Paperclip windows */ private _windows: LiveWindowManager; /** * manages commands coming in from VS Code */ private _commandManager: CommandManager; /** */ private _documentManager: DocumentManager; constructor(readonly context: ExtensionContext) { this._languageClient = new PaperclipLanguageClient(context); this._windows = new LiveWindowManager(this._languageClient); this._commandManager = new CommandManager(this._windows); this._documentManager = new DocumentManager( this._windows, this._languageClient ); } activate() { this._languageClient.activate(); this._windows.activate(); this._documentManager.activate(); this._commandManager.activate(); } dispose() { this._languageClient.dispose(); } } let _ext: PaperclipExtension; export const activate = (context: ExtensionContext) => { _ext = new PaperclipExtension(context); _ext.activate(); }; export const deactivate = () => { _ext.dispose(); }; ================================================ FILE: packages/paperclip-vscode/src/extension/language/client.ts ================================================ import { Disposable, RPCClientAdapter } from "@paperclip-ui/common"; import { workspace, ExtensionContext } from "vscode"; import { LanguageClient, ServerOptions, TransportKind, LanguageClientOptions, } from "vscode-languageclient"; import * as path from "path"; import { EventEmitter } from "events"; import { languageClientRPCAdapter } from "../rpc"; import { createListener } from "../utils"; import { designServerStartedChannel, revealSourceChannel, DesignServerStartedInfo, } from "../channels"; import { ExprSource } from "@paperclip-ui/utils"; // import { PCEngineCrashed } from "@tandem-ui/designer/lib/server/services/pc-engine"; /** * Spins up language server */ export class PaperclipLanguageClient implements Disposable { private _em: EventEmitter; private _client: LanguageClient; private _rpcClient: RPCClientAdapter; private _designServerStarted: ReturnType; private _revealSourceRequest: ReturnType; constructor(context: ExtensionContext) { this._em = new EventEmitter(); const serverPath = context.asAbsolutePath( path.join("lib", "extension", "language", "server", "index.js") ); const debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] }; const serverOptions: ServerOptions = { run: { module: serverPath, transport: TransportKind.ipc }, debug: { module: serverPath, transport: TransportKind.ipc, options: debugOptions, }, }; // Options to control the language client const clientOptions: LanguageClientOptions = { // Register the server for plain text documents documentSelector: [{ scheme: "file", language: "paperclip" }], synchronize: { configurationSection: ["paperclip", "credentials"], // Notify the server about file changes to '.clientrc files contained in the workspace fileEvents: workspace.createFileSystemWatcher("**/.clientrc"), }, }; this._client = new LanguageClient( "paperclipLanguageServer", "Paperclip Language Server", serverOptions, clientOptions ); } onRevealSourceRequest(listener: (info: ExprSource) => void) { return createListener(this._em, "revealSource", listener); } onDesignServerStarted(listener: (info: DesignServerStartedInfo) => void) { return createListener(this._em, "designServerStarted", listener); } private _onDesignServerStarted = async (info: DesignServerStartedInfo) => { console.log(`PaperclipLanguageClient::_onDesignServerStarted`); this._em.emit("designServerStarted", info); }; private _onRevealSourceRequest = async (info: ExprSource) => { console.log("PaperclipLanguageClient::onRevealSourceRequest"); this._em.emit("revealSource", info); }; async activate() { this._client.start(); await this.ready(); this._rpcClient = languageClientRPCAdapter(this._client); this._designServerStarted = designServerStartedChannel(this._rpcClient); this._designServerStarted.listen(this._onDesignServerStarted); this._revealSourceRequest = revealSourceChannel(this._rpcClient); this._revealSourceRequest.listen(this._onRevealSourceRequest); } ready() { return this._client.onReady(); } dispose() { this._client.stop(); } } ================================================ FILE: packages/paperclip-vscode/src/extension/language/index.ts ================================================ export * from "./client"; ================================================ FILE: packages/paperclip-vscode/src/extension/language/server/connection.ts ================================================ import { Connection, TextDocumentSyncKind, InitializeParams, WorkspaceFolder, DidChangeTextDocumentParams, DidOpenTextDocumentParams, DidCloseTextDocumentParams, } from "vscode-languageserver"; import { createListener, fixFileUrlCasing } from "../../utils"; import { TextDocument } from "vscode-languageserver-textdocument"; import { DocumentManager } from "./documents"; import { EventEmitter } from "stream"; import { languageClientRPCAdapter } from "../../rpc"; import { WorkspaceClient } from "@tandem-ui/workspace-client"; import { designServerStartedChannel, DesignServerStartedInfo, revealSourceChannel, } from "../../channels"; import { PaperclipDesignServer } from "./design-server"; // TODO - need better SRP here, this class is doing too much export class PaperclipLanguageServerConnectionManager { private _designServerStarted: ReturnType; private _revealSource: ReturnType; private _workspaceFolders: WorkspaceFolder[]; private _em: EventEmitter; constructor( private _designServer: PaperclipDesignServer, private _connection: Connection, private _documents: DocumentManager, readonly config: any ) { this._em = new EventEmitter(); } activate() { this._connection.onInitialize(this._onConnectionInitialize); this._connection.onInitialized(this._onConnectionInitialized); this._connection.onDidOpenTextDocument(this._onDidOpenTextDocument); this._connection.onDidCloseTextDocument(this._onDidCloseTextDocument); this._connection.onDidChangeTextDocument(this._onDidChangeTextDocument); const adapter = languageClientRPCAdapter(this._connection); this._designServerStarted = designServerStartedChannel(adapter); this._revealSource = revealSourceChannel(adapter); this._designServer.onStarted(this._onDesignServerStarted); this._designServer.onRevealSourceRequest((info) => { console.log(`On reveal source request`); this._revealSource.call(info); }); this._connection.listen(); } onInitialize(listener: (details: { workspaceFolders: string[] }) => void) { return createListener(this._em, "init", listener); } private _onDidOpenTextDocument = ({ textDocument, }: DidOpenTextDocumentParams) => { const uri = fixFileUrlCasing(textDocument.uri); this._documents.updateDocument( uri, TextDocument.create( uri, textDocument.languageId, textDocument.version, textDocument.text ) ); }; private _onDidCloseTextDocument = ({ textDocument, }: DidCloseTextDocumentParams) => { const uri = fixFileUrlCasing(textDocument.uri); this._documents.removeDocument(uri); }; private _onDidChangeTextDocument = ({ contentChanges, textDocument, }: DidChangeTextDocumentParams) => { const uri = fixFileUrlCasing(textDocument.uri); const oldDocument = this._documents.getDocument(uri); const newDocument = TextDocument.update( oldDocument, contentChanges, oldDocument.version + 1 ); this._documents.updateDocument(uri, newDocument); }; private _onDesignServerStarted = async (info: DesignServerStartedInfo) => { this._designServerStarted.call(info); }; private _onConnectionInitialize = (params: InitializeParams) => { this._workspaceFolders = params.workspaceFolders || []; return { capabilities: { textDocumentSync: TextDocumentSyncKind.Incremental as any, completionProvider: { resolveProvider: true, triggerCharacters: [".", "<", '"', "'", "{", ":", " ", "(", ">", "$"], }, documentLinkProvider: { resolveProvider: true, }, colorProvider: true, definitionProvider: true, }, }; }; private _onConnectionInitialized = async () => { this._em.emit("init", { workspaceFolders: this._workspaceFolders }); }; } ================================================ FILE: packages/paperclip-vscode/src/extension/language/server/design-server.ts ================================================ import { RPCClientAdapter } from "@paperclip-ui/common"; // eslint-disable-next-line const getPort = require("get-port"); import { start as startWorkspace, Workspace, Project, Server, } from "@tandem-ui/workspace/lib/server"; import { LogLevel } from "@paperclip-ui/common"; import { ExprSource } from "@paperclip-ui/utils"; import { EventEmitter } from "events"; import { createListener } from "../../utils"; import { DesignServerStartedInfo } from "../../channels"; export class PaperclipDesignServer { private _workspace: Workspace; private _project: Project; private _server: Server; private _port: number; private _em: EventEmitter; constructor() { this._em = new EventEmitter(); } onStarted(listener: (info: DesignServerStartedInfo) => void) { return createListener(this._em, "started", listener); } public start = async ({ workspaceFolders }) => { const server = (this._server = await startWorkspace({ logLevel: LogLevel.All, http: { port: (this._port = await getPort()), }, project: { installDependencies: false, }, adapter: { revealSource: this._revealSource, }, })); this._workspace = server.getWorkspace(); const cwd = workspaceFolders[0].uri; this._project = await this._workspace.start(cwd); this._project.commitAndPushChanges; this._em.emit("started", { projectId: this._project.getId(), httpPort: this._port, } as DesignServerStartedInfo); }; getCurrentProject() { return this._workspace.getProjectById(this._project.getId()); } getWorkspace() { return this._workspace; } onRevealSourceRequest = (listener: (source: ExprSource) => void) => { return createListener(this._em, "revealSourceRequest", listener); }; private _revealSource = (source: ExprSource) => { this._em.emit("revealSourceRequest", source); }; } ================================================ FILE: packages/paperclip-vscode/src/extension/language/server/documents.ts ================================================ import { TextEdit } from "vscode-languageserver"; import { Observable } from "@paperclip-ui/common"; import { TextDocument } from "vscode-languageserver-textdocument"; import { PaperclipDesignServer } from "./design-server"; export class DocumentManager { private _documents: Record; readonly events: Observable; constructor(private _designServer: PaperclipDesignServer) { this._documents = {}; this.events = new Observable(); } getDocument(uri: string) { return this._documents[uri]; } updateDocument(uri: string, document: TextDocument) { const exists = this._documents[uri] != null; this._documents[uri] = document; } appleDocumentEdits(uri: string, edits: TextEdit[]) { const text = TextDocument.applyEdits(this._documents[uri], edits); this._designServer .getCurrentProject() .getEngine() .updateVirtualFileContent(uri, text); } removeDocument(uri: string) { delete this._documents[uri]; } } ================================================ FILE: packages/paperclip-vscode/src/extension/language/server/events.ts ================================================ import { ExprSource } from "@paperclip-ui/utils"; import { WorkspaceFolder } from "vscode-languageserver"; import { Project } from "@tandem-ui/workspace/lib/server"; import { ContentChange } from "@paperclip-ui/source-writer"; import { EngineDelegate } from "@paperclip-ui/core"; // export class RevealSourceRequested { // static TYPE = "RevealSourceRequested"; // readonly type = RevealSourceRequested.TYPE; // constructor(readonly source: ExprSource) {} // toJSON() { // return { type: this.type, source: this.source }; // } // } // export class TextDocumentChanged { // static TYPE = "PaperclipLanguageServerConnection/TextDocumentChanged"; // readonly type = TextDocumentChanged.TYPE; // constructor(readonly uri: string, readonly content: string) {} // } // export class PCSourceEdited { // static TYPE = "PaperclipDesignServer/PCSourceEdited"; // readonly type = PCSourceEdited.TYPE; // constructor(readonly changes: Record) {} // toJSON() { // return { type: this.type, changes: this.changes }; // } // } // export class TextDocumentPreview { // static TYPE = "PaperclipLanguageServerConnection/TextDocumentPreview"; // readonly type = TextDocumentPreview.TYPE; // constructor(readonly uri: string, readonly content: string) {} // } // export class TextDocumentOpened { // static TYPE = "PaperclipLanguageServerConnection/TextDocumentOpened"; // readonly type = TextDocumentChanged.TYPE; // constructor(readonly uri: string, readonly content: string) {} // } // export class ProjectStarted { // static TYPE = "PaperclipDesignServer/ProjectStarted"; // readonly type = ProjectStarted.TYPE; // constructor(readonly project: Project) {} // } // export class UpdatedTextDocuments { // static TYPE = "PaperclipDesignServer/UpdatedTextDocuments"; // readonly type = TextDocumentChanged.TYPE; // } // export class PCContentChanged { // static TYPE = "PaperclipDesignServer/PCContentChanged"; // readonly type = PCContentChanged.TYPE; // readonly url: string; // readonly content: string; // } ================================================ FILE: packages/paperclip-vscode/src/extension/language/server/index.ts ================================================ // TODO: need to simplify event flow because it's a cluster F import { createConnection, ProposedFeatures } from "vscode-languageserver"; import { PaperclipLanguageServerConnectionManager } from "./connection"; import { PaperclipDesignServer } from "./design-server"; import { DocumentManager } from "./documents"; import { LanguageRequestResolver } from "./resolver"; import { languageClientRPCAdapter } from "../../rpc"; export class PaperclipLanguageServer { constructor() { const connection = createConnection(ProposedFeatures.all); const designServer = new PaperclipDesignServer(); const documents = new DocumentManager(designServer); const connectionManager = new PaperclipLanguageServerConnectionManager( designServer, connection, documents, {} ); new LanguageRequestResolver(designServer, connection, documents); connectionManager.activate(); connectionManager.onInitialize((info) => { designServer.start(info); }); } } const server = new PaperclipLanguageServer(); ================================================ FILE: packages/paperclip-vscode/src/extension/language/server/resolver.ts ================================================ import { ColorPresentationRequest, CompletionRequest, CompletionResolveRequest, DocumentLink, Connection, DefinitionRequest, DocumentColorParams, DocumentColorRequest, TextEdit, Range, Color, DefinitionLink, ColorPresentation, DocumentLinkRequest, ColorPresentationParams, DefinitionParams, DocumentLinkParams, CompletionParams, } from "vscode-languageserver"; import * as fs from "fs"; import { deferPromise } from "@paperclip-ui/common"; import { TextDocument } from "vscode-languageserver-textdocument"; import { PaperclipLanguageService } from "@paperclip-ui/language-service"; import { fixFileUrlCasing } from "../../utils"; import { DocumentManager } from "./documents"; import * as parseColor from "color"; import { stripFileProtocol } from "@paperclip-ui/utils"; import { PaperclipDesignServer } from "./design-server"; import { DesignServerStartedInfo } from "../../channels"; import { LintInfo } from "@paperclip-ui/language-service/src/error-service"; export class LanguageRequestResolver { private _resolveService: (service: PaperclipLanguageService) => void; private _service: Promise; private _listening: boolean; private _settingColor: boolean; private _latestColor: [string, ColorPresentation]; constructor( private _designServer: PaperclipDesignServer, private _connection: Connection, private _documents: DocumentManager ) { [this._service, this._resolveService] = deferPromise(); this._designServer.onStarted(this._onDesignServerStarted); this._listen(); } private _onDesignServerStarted = (options: DesignServerStartedInfo) => { const service = new PaperclipLanguageService( this._designServer.getCurrentProject().getEngine() ); this._resolveService(service); service.onLinted(this._onLinted); }; private _listen() { if (this._listening) { return; } this._listening = true; this._connection.onRequest( ColorPresentationRequest.type, this._onColorPresentationRequest ); this._connection.onRequest( DocumentColorRequest.type, this._onDocumentColorRequest ); this._connection.onRequest( CompletionRequest.type, this._onCompletionRequest ); this._connection.onRequest( CompletionResolveRequest.type, this._onCompletionResolveRequest ); this._connection.onRequest( DefinitionRequest.type, this._onDefinitionRequest ); this._connection.onRequest( DocumentLinkRequest.type, this._onDocumentLinkRequest ); } private _onLinted = (info: LintInfo) => { const { uri, content, diagnostics } = info; let textDocument = this._documents.getDocument(uri); textDocument = TextDocument.create(uri, "paperclip", 0, content); this._connection.sendDiagnostics({ uri: uri, diagnostics: diagnostics.map((diagnostic) => { return { ...diagnostic, range: { start: textDocument.positionAt(diagnostic.range.start.pos), end: textDocument.positionAt(diagnostic.range.end.pos), }, }; }), }); }; private _onColorPresentationRequest = async ( params: ColorPresentationParams ) => { const presentation = getColorPresentation(params.color, params.range); const uri = fixFileUrlCasing(params.textDocument.uri); this._applyColorPreview(uri, presentation); return [presentation]; }; private _applyColorPreview = ( uri: string, presentation: ColorPresentation ) => { if (this._settingColor) { this._latestColor = [uri, presentation]; return false; } this._settingColor = true; this._documents.appleDocumentEdits(uri, [presentation.textEdit]); setTimeout(() => { this._settingColor = false; if (this._latestColor) { const [uri, presentation] = this._latestColor; this._latestColor = null; this._applyColorPreview(uri, presentation); } }, 50); }; private _onDocumentColorRequest = async (params: DocumentColorParams) => { const uri = fixFileUrlCasing(params.textDocument.uri); const document = this._documents.getDocument(uri); return (await this._service) .getDocumentColors(uri) .map(({ value, start, end }) => { return { range: { start: document.positionAt(start), end: document.positionAt(end), }, color: value, }; }); }; private _onCompletionResolveRequest = async (item) => { return item; }; private _onCompletionRequest = async (params: CompletionParams) => { const uri = fixFileUrlCasing(params.textDocument.uri); const document = this._documents.getDocument(uri); const items = (await this._service).getAutoCompletionSuggestions( uri, document.offsetAt(params.position) ); return items; }; private _onDefinitionRequest = async (params: DefinitionParams) => { const uri = fixFileUrlCasing(params.textDocument.uri); const document = this._documents.getDocument(uri); const info = (await this._service) .getDefinitions(uri) .filter((info) => { const offset = document.offsetAt(params.position); return ( offset >= info.instanceRange.start.pos && offset <= info.instanceRange.end.pos ); }) .map( ({ sourceUri, instanceRange: { start: { pos: instanceStart }, end: { pos: instanceEnd }, }, sourceRange: { start: { pos: sourceStart }, end: { pos: sourceEnd }, }, sourceDefinitionRange: { start: { pos: definitionStart }, end: { pos: definitionEnd }, }, }) => { const sourceDocument = this._documents.getDocument(sourceUri) || TextDocument.create( sourceUri, "paperclip", null, fs.readFileSync(stripFileProtocol(sourceUri), "utf8") ); return { targetUri: sourceDocument.uri, targetRange: { start: sourceDocument.positionAt(definitionStart), end: sourceDocument.positionAt(definitionEnd), }, targetSelectionRange: { start: sourceDocument.positionAt(sourceStart), end: sourceDocument.positionAt(sourceEnd), }, originSelectionRange: { start: document.positionAt(instanceStart), end: document.positionAt(instanceEnd), }, }; } ) as DefinitionLink[]; return info; }; private _onDocumentLinkRequest = async (params: DocumentLinkParams) => { const uri = fixFileUrlCasing(params.textDocument.uri); const document = this._documents.getDocument(uri); return (await this._service) .getLinks(uri) .map(({ uri, range: { start, end } }) => ({ target: uri, range: { start: document.positionAt(start.pos), end: document.positionAt(end.pos), }, })) as DocumentLink[]; }; } // from https://github.com/microsoft/vscode-css-languageservice/blob/a652e5da7ebb86677bff750c9ca0cf4740adacee/src/services/cssNavigation.ts#L196 const getColorPresentation = ( { red, green, blue, alpha }: Color, range: Range ): ColorPresentation => { const info = parseColor.rgb( Math.round(red * 255), Math.round(green * 255), Math.round(blue * 255), alpha ); const label = info.toString(); return { label, textEdit: TextEdit.replace(range, label) }; }; ================================================ FILE: packages/paperclip-vscode/src/extension/preview/index.ts ================================================ ================================================ FILE: packages/paperclip-vscode/src/extension/preview/live-window-manager.ts ================================================ import { WebviewPanel, window } from "vscode"; import { LiveWindow, LiveWindowState } from "./live-window"; import { isPaperclipFile } from "@paperclip-ui/utils"; import { PaperclipLanguageClient } from "../language"; import { DesignServerStartedInfo } from "../channels"; export class LiveWindowManager { private _windows: LiveWindow[]; private _devServerPort: number; private _projectId: string; constructor(private _languageClient: PaperclipLanguageClient) { this._windows = []; this._languageClient.onDesignServerStarted(this._onDevServerStarted); } _onDevServerStarted = ({ httpPort, projectId }: DesignServerStartedInfo) => { this._devServerPort = httpPort; this._projectId = projectId; for (const window of this._windows) { window.setDevServerInfo(httpPort, projectId); } }; getLength() { return this._windows.length; } setStickyWindowUri(uri: string) { const stickyWindow = this._windows.find( (window) => window.getState().sticky ); if (stickyWindow && isPaperclipFile(uri)) { stickyWindow.setTargetUri(uri); return true; } return false; } open(uri: string, sticky: boolean) { const liveWindow = LiveWindow.newFromUri( uri, sticky, this._devServerPort, this._projectId ); this._add(liveWindow); } private _add(window: LiveWindow) { this._windows.push(window); window.onDispose(() => { this._windows.splice(this._windows.indexOf(window), 1); }); } activate() { window.registerWebviewPanelSerializer(LiveWindow.TYPE, { deserializeWebviewPanel: async ( panel: WebviewPanel, state: LiveWindowState ) => { this._add( LiveWindow.newFromPanel( panel, state, this._devServerPort, this._projectId ) ); }, }); } } ================================================ FILE: packages/paperclip-vscode/src/extension/preview/live-window.ts ================================================ import { window, WebviewPanel, ViewColumn } from "vscode"; import * as path from "path"; import { ImmutableStore } from "@paperclip-ui/common"; import { EventEmitter } from "events"; import * as qs from "querystring"; type LiveWindowLocation = { pathname: string; query: Partial<{ canvasFile: string; embedded: boolean; id: string; expanded: boolean; frame: number; }>; }; export type LiveWindowState = { location: LiveWindowLocation; sticky: boolean; panelVisible?: boolean; }; export class LiveWindow { static TYPE = "@paperclip-ui/preview"; private _store: ImmutableStore; private _em: EventEmitter; constructor( state: LiveWindowState, private _devServerPort: number, private _projectId: string, private _panel: WebviewPanel ) { this._store = new ImmutableStore({ ...state, embedded: true, panelVisible: this._panel.visible, }); this._store.update((newState) => { newState.location.query.id = `${Date.now()}.${Math.random()}`; }); this._store.onChange(this._onStoreChange); this._em = new EventEmitter(); this._panel.onDidDispose(this._onPanelDispose); this._createBindings(); this._render(); } getState() { return this._store.getState(); } dispose() { try { this._panel.dispose(); // eslint-disable-next-line } catch (e) {} this._em.emit("dispose"); } onDispose(listener: () => void) { this._em.once("dispose", listener); } /** * Need to make this accessble in case language server respawns * TODO - probably need to change to be event based instead. */ setDevServerInfo(port: number, projectId: string) { this._devServerPort = port; this._projectId = projectId; this._render(); } setTargetUri(uri: string) { this._store.update((state) => { state.location.query.canvasFile = uri; }); } private _onPanelDispose = () => { this.dispose(); }; private _onStoreChange = ( newState: LiveWindowState, oldState: LiveWindowState ) => { // if coming into visibility again if ( (newState.panelVisible && newState.panelVisible !== oldState.panelVisible) || newState.location.query.canvasFile !== oldState.location.query.canvasFile ) { this._render(); } }; private _render() { const state = this.getState(); this._panel.title = `⚡️ ${ state.sticky ? "sticky preview" : path.basename(state.location.query.canvasFile) }`; this._panel.webview.html = ""; this._panel.webview.html = this._getHTML(); } private _getHTML() { const state = this.getState(); return `
    `; } private _createBindings() { this._panel.onDidChangeViewState(() => { this._store.update((state) => { state.panelVisible = this._panel.visible; }); }); } static newFromUri( uri: string, sticky: boolean, devServerPort: number, projectId: string ) { const panel = window.createWebviewPanel( LiveWindow.TYPE, sticky ? "sticky preview" : `⚡️ ${path.basename(uri)}`, ViewColumn.Beside, { enableScripts: true, } ); return new LiveWindow( { location: getLocationFromUri(uri), sticky }, devServerPort, projectId, panel ); } static newFromPanel( panel: WebviewPanel, state: LiveWindowState, devServerPort: number, projectId: string ) { return new LiveWindow(state, devServerPort, projectId, panel); } } const getLocationFromUri = (uri: string): LiveWindowLocation => ({ pathname: "/", query: { canvasFile: uri, }, }); ================================================ FILE: packages/paperclip-vscode/src/extension/preview/service.ts ================================================ export class PaperclipPreviewService {} ================================================ FILE: packages/paperclip-vscode/src/extension/rpc/index.ts ================================================ import { RPCClientAdapter } from "@paperclip-ui/common"; import { createListener } from "../utils"; import { LanguageClient } from "vscode-languageclient"; import { EventEmitter } from "events"; import { Connection } from "vscode-languageserver"; export const languageClientRPCAdapter = ( client: LanguageClient | Connection ): RPCClientAdapter => { const em = new EventEmitter(); client.onNotification("$$MESSAGE", (message) => em.emit("message", message)); return { send(message) { client.sendNotification("$$MESSAGE", message); }, onMessage(listener: (message: any) => void) { return createListener(em, "message", listener); }, onDisconnect() { // eslint-disable-next-line return () => {}; }, }; }; ================================================ FILE: packages/paperclip-vscode/src/extension/utils.ts ================================================ import * as fs from "fs"; import * as url from "url"; export const fixFileUrlCasing = (href: string) => { if (href.indexOf("file://") !== 0) { return href; } const caseSensitivePath = fs.realpathSync.native(url.fileURLToPath(href)); return url.pathToFileURL(caseSensitivePath).href; }; export const createListener = ( em: any, type: string, listener: (...args: any[]) => void ) => { em.on(type, listener); return () => em.off(type, listener); }; ================================================ FILE: packages/paperclip-vscode/src/extension/version-check.ts ================================================ // import { workspace, window } from "vscode"; // import * as fs from "fs"; // const getMajorVersion = semver => { // const v = (semver.match(/(\d+)/) || [])[1]; // return v && Number(v); // }; // import * as findUp from "find-up"; // // eslint-disable-next-line // const selfPkg = require("../../package"); // /** // * Ensures that the VS Code extension & whatever's installed // * as a dep are a match. // */ // import { ExtensionContext } from "vscode"; // export const checkVersionMatch = (context: ExtensionContext) => { // const packagePath = findUp.sync("package.json", { cwd: workspace.rootPath }); // if (!packagePath) { // return; // } // const pkg = JSON.parse(fs.readFileSync(packagePath, "utf8")); // const pcCLIVersion = // (pkg.devDependencies || {})["@paperclip-ui/cli"] || // (pkg.dependencies || {})["@paperclip-ui/cli"]; // // paperclip not installed // if (!pcCLIVersion) { // return; // } // const cliMajor = getMajorVersion(pcCLIVersion); // // maybe *? Ignore. // if (!cliMajor) { // return cliMajor; // } // const selfMajor = getMajorVersion(selfPkg.version); // if (selfMajor !== cliMajor) { // const details = `(extension version: v${ // selfPkg.version // }, dependency version: v${pcCLIVersion.replace(/[^\wd.]/g, "")})`; // let message = ""; // if (selfMajor > cliMajor) { // message = `Paperclip: this extension requires newer Paperclip dependencies ${details}. You \u00a0 need to upgrade your Paperclip dependencies, or download a different version of the extension that matches them (v${cliMajor}.x.x).`; // } else { // message = `Paperclip: your extension needs to updated ${details}.`; // } // window.showWarningMessage(message, "ok"); // } // }; ================================================ FILE: packages/paperclip-vscode/src/scripts/build.ts ================================================ // From https://github.com/microsoft/TypeScript-TmLanguage/blob/master/build/build.ts import fs = require("fs"); import path = require("path"); import yaml = require("js-yaml"); import plist = require("plist"); enum Language { Paperclip = "paperclip-html" } enum Extension { YamlTmLanguage = "yaml", JsonTmLanguage = "tmLanguage.json" } function file(language: Language, extension: Extension) { return path.join( __dirname, "..", "..", "syntaxes", `${language}.${extension}` ); } // function writePlistFile(grammar: TmGrammar | TmTheme, fileName: string) { // const text = plist.build(grammar); // // fs.writeFileSync(fileName, text); // } function writeJSONFile(grammar: TmGrammar | TmTheme, fileName: string) { const text = JSON.stringify(grammar, null, 2); fs.writeFileSync(fileName, text); } function readYaml(fileName: string) { const text = fs.readFileSync(fileName, "utf8"); return yaml.load(text); } // function changeTsToTsx(str: string) { // return str.replace(/\.ts/g, '.tsx'); // } function transformGrammarRule( rule: any, propertyNames: string[], transformProperty: (ruleProperty: string) => string ) { for (const propertyName of propertyNames) { const value = rule[propertyName]; if (typeof value === "string") { rule[propertyName] = transformProperty(value); } } for (const propertyName in rule) { const value = rule[propertyName]; if (typeof value === "object") { transformGrammarRule(value, propertyNames, transformProperty); } } } function transformGrammarRepository( grammar: TmGrammar, propertyNames: string[], transformProperty: (ruleProperty: string) => string ) { const repository = grammar.repository; for (const key in repository) { transformGrammarRule(repository[key], propertyNames, transformProperty); } } // function getTsxGrammar() { // let variables: MapLike; // const tsxUpdatesBeforeTransformation = readYaml(file(Language., Extension.YamlTmLanguage)) as TmGrammar; // const grammar = getTsGrammar(tsGrammarVariables => { // variables = tsGrammarVariables; // for (const variableName in tsxUpdatesBeforeTransformation.variables) { // variables[variableName] = tsxUpdatesBeforeTransformation.variables[variableName]; // } // return variables; // }); // const tsxUpdates = updateGrammarVariables(tsxUpdatesBeforeTransformation, variables!); // // Update name, file types, scope name and uuid // grammar.name = tsxUpdates.name; // grammar.scopeName = tsxUpdates.scopeName; // grammar.fileTypes = tsxUpdates.fileTypes; // grammar.uuid = tsxUpdates.uuid; // // Update scope names to .tsx // transformGrammarRepository(grammar, ["name", "contentName"], changeTsToTsx); // // Add repository items // const repository = grammar.repository; // const updatesRepository = tsxUpdates.repository; // for (let key in updatesRepository) { // switch(key) { // case "expressionWithoutIdentifiers": // // Update expression // (repository[key] as TmGrammarRepositoryPatterns).patterns.unshift((updatesRepository[key] as TmGrammarRepositoryPatterns).patterns[0]); // break; // default: // // Add jsx // repository[key] = updatesRepository[key]; // } // } // return grammar; // } function getTsGrammar( getVariables: (tsGrammarVariables: MapLike) => MapLike ) { const tsGrammarBeforeTransformation = readYaml( file(Language.Paperclip, Extension.YamlTmLanguage) ) as TmGrammar; return updateGrammarVariables( tsGrammarBeforeTransformation, getVariables(tsGrammarBeforeTransformation.variables as MapLike) ); } function replacePatternVariables( pattern: string, variableReplacers: VariableReplacer[] ) { let result = pattern; for (const [variableName, value] of variableReplacers) { result = result.replace(variableName, value); } return result; } type VariableReplacer = [RegExp, string]; function updateGrammarVariables( grammar: TmGrammar, variables: MapLike ) { delete grammar.variables; const variableReplacers: VariableReplacer[] = []; for (const variableName in variables) { // Replace the pattern with earlier variables const pattern = replacePatternVariables( variables[variableName], variableReplacers ); variableReplacers.push([new RegExp(`{{${variableName}}}`, "gim"), pattern]); } transformGrammarRepository(grammar, ["begin", "end", "match"], pattern => replacePatternVariables(pattern, variableReplacers) ); return grammar; } function buildGrammar() { const pcGrammar = getTsGrammar(grammarVariables => grammarVariables); // Write TypeScript.tmLanguage // writePlistFile(pcGrammar, file(Language.Paperclip, Extension.YamlTmLanguage)); writeJSONFile(pcGrammar, file(Language.Paperclip, Extension.JsonTmLanguage)); // Write TypeScriptReact.tmLangauge // const tsxGrammar = getTsxGrammar(); // writePlistFile(tsxGrammar, file(Language.TypeScriptReact, Extension.TmLanguage)); } // function changeTsToTsxTheme(theme: TmTheme) { // const tsxUpdates = readYaml(file(Language.TypeScriptReact, Extension.YamlTmTheme)) as TmTheme; // // Update name, uuid // theme.name = tsxUpdates.name; // theme.uuid = tsxUpdates.uuid; // // Update scope names to .tsx // const settings = theme.settings; // for (let i = 0; i < settings.length; i++) { // settings[i].scope = changeTsToTsx(settings[i].scope); // } // // Add additional setting items // theme.settings = theme.settings.concat(tsxUpdates.settings); // return theme; // } // function buildTheme() { // const tsTheme = readYaml(file(Language.TypeScript, Extension.YamlTmTheme)) as TmTheme; // // Write TypeScript.tmTheme // writePlistFile(tsTheme, file(Language.TypeScript, Extension.TmTheme)); // // Write TypeScriptReact.thTheme // const tsxTheme = changeTsToTsxTheme(tsTheme); // writePlistFile(tsxTheme, file(Language.TypeScriptReact, Extension.TmTheme)); // } buildGrammar(); // buildTheme(); ================================================ FILE: packages/paperclip-vscode/src/scripts/theme.ts ================================================ declare module "plist" { export function build(json: any): string; } declare interface MapLike { [s: string]: T; } declare interface TmGrammarRuleName { name: string; } declare interface TmGrammarRule { name?: string; } declare interface TmGrammarMatchRule extends TmGrammarRule { match: string; captures: MapLike; } declare interface TmGrammarBeginEndRule extends TmGrammarRule { contentName?: string; begin: string; end: string; beginCaptures?: MapLike; endCaptures?: MapLike; patterns: AnyTmGrammarRule[]; } declare interface TmGrammarIncludeRule extends TmGrammarRule { include: string; } declare type AnyTmGrammarRule = | TmGrammarMatchRule | TmGrammarBeginEndRule | TmGrammarIncludeRule; declare interface TmGrammarRepositoryPatterns { patterns: AnyTmGrammarRule[]; } declare type TmGrammarRepositaryRule = | AnyTmGrammarRule | TmGrammarRepositoryPatterns; declare interface TmGrammar { name: string; scopeName: string; fileTypes: string[]; uuid: string; variables?: MapLike; patterns?: AnyTmGrammarRule[]; repository: MapLike; } declare interface TmThemeSetting { scope: string; settings: { vsclassificationtype: string }; } declare interface TmTheme { name: string; uuid: string; settings: TmThemeSetting[]; } ================================================ FILE: packages/paperclip-vscode/syntaxes/highlight-test.pc ================================================
    this is another test
    child
    ================================================ FILE: packages/paperclip-vscode/syntaxes/paperclip-css.json ================================================ { "patterns": [ { "patterns": [ { "include": "#comment-tag" } ], "begin": "/\\*", "name": "comment.block.pcss", "end": "\\*/" }, { "include": "#double-slash" }, { "include": "#double-quoted" }, { "include": "#single-quoted" }, { "include": "#interpolation" }, { "include": "#placeholder-selector" }, { "include": "#variable" }, { "include": "#variable-root-css" }, { "include": "#numeric" }, { "include": "#unit" }, { "include": "#flag" }, { "include": "#dotdotdot" }, { "captures": { "0": { "name": "keyword.control.at-rule.css.pcss" } }, "begin": "@include", "name": "support.function.name.pcss.library", "end": "(?=\\n|\\(|{|;)" }, { "captures": { "0": { "name": "keyword.control.at-rule.css.pcss" } }, "patterns": [ { "name": "entity.name.function", "match": "[\\w-]+" } ], "begin": "@mixin|@function", "name": "support.function.name.pcss.no-completions", "end": "$\\n?|(?=\\(|{)" }, { "name": "string.quoted.double.css.pcss", "match": "(?<=@import)\\s[\\w/.*-]+" }, { "begin": "@", "name": "keyword.control.at-rule.css.pcss", "end": "$\\n?|\\s(?!(all|braille|embossed|handheld|print|projection|screen|speech|tty|tv|if|only|not)(\\s|,))|(?=;)" }, { "patterns": [ { "include": "#interpolation" }, { "include": "#pseudo-class" } ], "begin": "#", "name": "entity.other.attribute-name.id.css.pcss", "end": "$\\n?|(?=\\s|,|;|\\(|\\)|\\.|\\[|{|>)" }, { "patterns": [ { "include": "#interpolation" }, { "include": "#pseudo-class" } ], "begin": "\\.|(?<=&)(-|_)", "name": "entity.other.attribute-name.class.css.pcss", "end": "$\\n?|(?=\\s|,|;|\\(|\\)|\\[|{|>)" }, { "patterns": [ { "include": "#double-quoted" }, { "include": "#single-quoted" }, { "name": "keyword.other.regex.pcss", "match": "\\^|\\$|\\*|~" } ], "begin": "\\[", "name": "entity.other.attribute-selector.pcss", "end": "\\]" }, { "name": "entity.other.attribute-name.pseudo-class.css.pcss", "match": "(?<=\\]|\\)|not\\(|\\*|>|>\\s):[a-z:-]+|(::|:-)[a-z:-]+" }, { "patterns": [ { "include": "#double-slash" }, { "include": "#double-quoted" }, { "include": "#single-quoted" }, { "include": "#interpolation" }, { "include": "#variable" }, { "include": "#rgb-value" }, { "include": "#numeric" }, { "include": "#unit" }, { "include": "#flag" }, { "include": "#function" }, { "include": "#function-content" }, { "include": "#function-content-var" }, { "include": "#operator" }, { "include": "#parent-selector" }, { "include": "#property-value" } ], "begin": ":", "name": "meta.property-list.css.pcss", "end": "$\\n?|(?=;|\\s\\(|and\\(|{|}|\\),)" }, { "include": "#rgb-value" }, { "include": "#function" }, { "include": "#function-content" }, { "patterns": [ { "include": "#interpolation" }, { "include": "#pseudo-class" } ], "begin": "(?|-|_)" }, { "include": "#operator" }, { "name": "support.type.property-name.css.pcss", "match": "[a-z-]+((?=:|#{))" }, { "include": "#reserved-words" }, { "include": "#property-value" } ], "name": "pcss", "repository": { "reserved-words": { "name": "support.type.property-name.css.pcss", "match": "\\b(false|from|in|not|null|through|to|true)\\b" }, "double-quoted": { "patterns": [ { "include": "#quoted-interpolation" } ], "begin": "\"", "name": "string.quoted.double.css.pcss", "end": "\"" }, "operator": { "name": "keyword.operator.pcss", "match": "\\+|\\s-\\s|\\s-(?=\\$)|(?<=\\()-(?=\\$)|\\s-(?=\\()|\\*|/|%|=|!|<|>|~" }, "function-content": { "name": "string.quoted.double.css.pcss", "match": "(?<=url\\(|format\\(|attr\\().+?(?=\\))" }, "double-slash": { "patterns": [ { "include": "#comment-tag" } ], "begin": "//", "name": "comment.line.pcss", "end": "$" }, "numeric": { "name": "constant.numeric.css.pcss", "match": "(-|\\.)?[0-9]+(\\.[0-9]+)?" }, "variable-root-css": { "name": "variable.parameter.pcss", "match": "(?)", "endCaptures": { "1": { "name": "punctuation.definition.tag.end.html" } } }, { "begin": "(<)(style)([^>]*)(>)", "contentName": "source.pcss", "beginCaptures": { "1": { "name": "punctuation.definition.tag.begin.html" }, "2": { "name": "entity.name.tag.html" }, "3": { "patterns": [ { "include": "#attribute" } ] }, "4": { "name": "punctuation.definition.tag.end.html" } }, "end": "()", "endCaptures": { "1": { "name": "punctuation.definition.tag.end.html" }, "2": { "name": "entity.name.tag.html" }, "3": { "name": "punctuation.definition.tag.end.html" } }, "patterns": [ { "include": "source.pcss" } ] }, { "match": "(<)([\\w\\-\\.]+)(>)", "captures": { "1": { "name": "punctuation.definition.tag.begin.html" }, "2": { "name": "entity.name.tag.html" } } } ] }, "attribute": { "patterns": [ { "begin": "(?<=\\s)(component|export)(?=\\s)", "beginCaptures": { "1": { "name": "keyword.control" } }, "end": "(?=\\s*+[^=\\s])" }, { "begin": "\\s(as)(?=\\=)", "beginCaptures": { "1": { "name": "keyword.control" } }, "end": "(?=\\s*+[^=\\s])", "patterns": [ { "include": "#attributes-interior" } ] }, { "begin": "([\\w\\$_\\-]+)", "beginCaptures": { "1": { "name": "entity.other.attribute-name.html" } }, "end": "(?=\\s*+[^=\\s])", "patterns": [ { "include": "#attributes-interior" } ] }, { "include": "#slots" } ] }, "attributes-interior": { "patterns": [ { "begin": "(=)", "beginCaptures": { "1": { "name": "punctuation.separator.key-value.html" } }, "end": "(?<=[^\\s=])(?!\\s*=)|(?=/?>)", "patterns": [ { "include": "#dynamic-strings" }, { "include": "#slots" } ] }, { "include": "#slots" } ] }, "comments": { "patterns": [ { "name": "comment.block.html", "begin": "()", "endCaptures": { "1": { "name": "punctuation.definition.comment.html" } } } ] }, "slots": { "patterns": [ { "begin": "({)", "beginCaptures": { "1": { "name": "punctuation.definition.tag.begin.html" } }, "end": "(})", "endCaptures": { "1": { "name": "punctuation.definition.tag.end.html" } }, "patterns": [ { "include": "#operators" }, { "include": "#nodes" }, { "include": "#dynamic-strings" } ] } ] }, "operators": { "patterns": [ { "match": "(&&|!|\\|\\|)", "name": "keyword.control" } ] }, "blocks": { "patterns": [ { "begin": "({\\#)", "beginCaptures": { "1": { "name": "punctuation" } }, "end": "(})", "endCaptures": { "1": { "name": "punctuation" } }, "patterns": [ { "include": "#block-interior" } ] }, { "match": "({/})", "name": "punctuation" }, { "begin": "(({/)(else)\\s)", "beginCaptures": { "2": { "name": "punctuation" }, "3": { "name": "keyword.control" } }, "end": "(})", "endCaptures": { "1": { "name": "punctuation" } }, "patterns": [ { "include": "#block-interior" } ] }, { "name": "string.quoted.double", "match": "({/)(else)(})", "captures": { "1": { "name": "punctuation" }, "2": { "name": "keyword.control" }, "3": { "name": "punctuation" } } } ] }, "block-interior": { "patterns": [ { "name": "punctuation.definition.comment.html", "begin": "(if|each)", "beginCaptures": { "1": { "name": "keyword.control" } }, "end": "(?=})", "patterns": [ { "include": "source.js" } ] } ] }, "dynamic-strings": { "patterns": [ { "name": "string.quoted.double", "begin": "\"", "end": "\"", "beginCaptures": { "1": { "name": "punctuation.separator.key-value.html" } }, "patterns": [ { "include": "#class-pierce" }, { "include": "#slots" } ] }, { "name": "string.quoted.single", "begin": "'", "end": "'", "beginCaptures": { "1": { "name": "punctuation.separator.key-value.html" } }, "patterns": [ { "include": "#class-pierce" }, { "include": "#slots" } ] } ] }, "class-pierce": { "patterns": [ { "match": "(>>>|\\$)([^\\s\"'>{]+)", "captures": { "0": { "name": "keyword.control" } } } ] }, "strings": { "patterns": [ { "name": "string.quoted.double", "begin": "\"", "end": "\"" }, { "name": "string.quoted.single", "begin": "'", "end": "'" } ] }, "entities": { "patterns": [ { "match": "(&)([^;]+?)(;)", "captures": { "1": { "name": "punctuation.definition.entity.html" }, "2": { "name": "constant.character.entity.named.$2.html" }, "3": { "name": "punctuation.definition.entity.html" } } } ] } } } ================================================ FILE: packages/paperclip-vscode/syntaxes/paperclip-html.yaml ================================================ # [PackageDev] target_format: json, ext: tmLanguage.json # https://github.com/microsoft/vscode/blob/master/extensions/html/syntaxes/html.tmLanguage.json # https://github.com/microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.YAML-tmLanguage # TODOS # HTML entities ("e;) name: Paperclip Template scopeName: text.html.paperclip-html fileTypes: [] uuid: 3e3ce414-c385-42ea-a2ba-451a37ca6fd1 patterns: - include: "#topNodes" variables: tagName: ([\w\-\.]+) tagOpen: (<){{tagName}}(?=((<\s*)|(\s+))(?!\?)|\/?>) # tagOpen: (<){{tagName}}(?=((<\s*)|(\s+))(?!\?)|\/?>) tagClose: () tagOpenLookahead: (?={{tagOpen}}) repository: topNodes: patterns: - include: "#nodes" nodes: patterns: - include: "#elements" - include: "#blocks" - include: "#comments" - include: "#entities" - include: "#slots" elements: patterns: # Void tag elements - begin: (<)(meta|import) beginCaptures: "1": { name: punctuation.definition.tag.begin.html } "2": { name: entity.name.tag.html } patterns: - include: "#attribute" end: (/?>) endCaptures: "1": { name: punctuation.definition.tag.end.html } #
    B
    `, ], ], [ "Properly replaces style rules", { "entry.pc": `a`, }, [ { "entry.pc": `a`, }, `
    a
    `, ], ], [ "Properly removes style rules", { "entry.pc": `a`, }, [ { "entry.pc": `a`, }, `
    a
    `, ], ], [ "Inserts new rules", { "entry.pc": `a`, }, [ { "entry.pc": `a`, }, `
    a
    `, ], ], [ "Updates CSS from module", { "entry.pc": ``, "module.pc": `
    `, }, [ { "module.pc": `
    `, }, `
    `, ], ], [ "Updates CSS from module _module_", { "entry.pc": ``, "a.pc": ` `, "b.pc": `
    `, }, [ { "b.pc": `
    `, }, `
    `, ], ], [ "Can add new frames & still maintain styles", { "entry.pc": `
    Test
    `, }, [ { "entry.pc": `
    Test A
    Test B
    `, }, `
    Test A
    Test B
    `, ], ], [ "Properly patches between", { "entry.pc": ` `, }, [ { "entry.pc": ` `, }, "", ], [ { "entry.pc": ` `, }, `
    `, ], ], [ "Maintains frame with previous sibling is hidden", { "entry.pc": ` `, }, [ { "entry.pc": ` `, }, `
    `, ], ], [ "Can handle charset", { "entry.pc": ` `, }, [ { "entry.pc": ` `, }, "", ], ], [ "Can handle charset", { "entry.pc": `
    `, }, [ { "entry.pc": `
    `, }, `
    `, ], ], [ "Can handle CSS file changes", { "entry.pc": ` `, "test.css": ` div { color: red; } `, }, [ { "entry.pc": `
    `, "test.css": ` div { color: blue; } `, }, `
    `, ], ], [ "Adds styles if import is added of module that is already loaded", { "entry.pc": ` `, "module.pc": ` `, }, [ { "entry.pc": ` `, }, `
    `, ], ], [ "Adds styles from dependency dependency", { "entry.pc": `a`, "module.pc": ``, "module2.pc": ``, }, [ { "entry.pc": ``, "module.pc": ``, }, `
    `, ], ], [ "removes styles", { "entry.pc": ` a`, "module-a.pc": ``, "module-b.pc": ``, "module-c.pc": ``, "module-d.pc": ``, }, [ { "entry.pc": ` a`, }, `
    a
    `, ], ], [ "Properly replaces elements", { "entry.pc": `
    Content `, }, [ { "entry.pc": `
    Content `, }, `
    `, ], ], [ "Doesn't remove previous class if set", { "entry.pc": ` `, }, [ { "entry.pc": ` `, }, `
    `, ], [ { "entry.pc": ` `, }, `
    `, ], ], [ "Properly replaces rule", { "entry.pc": `
    `, }, [ { "entry.pc": `
    `, }, `
    `, ], ], ].forEach(([title, initial, ...changes]: any) => { it(title, async () => { const engine = createMockEngine(initial); let frames = renderFrames( (await engine.open("entry.pc")) as LoadedPCData, { domFactory: mockDOMFactory } ); for (const [change, expectationSanityCheck] of changes) { const prevData = engine.open("entry.pc") as LoadedPCData; for (const name in change) { await engine.updateVirtualFileContent(name, change[name]); } const baselineFrames = renderFrames( (await engine.open("entry.pc")) as LoadedPCData, { domFactory: mockDOMFactory } ); frames = patchFrames( frames, prevData, engine.open("entry.pc") as LoadedPCData, { domFactory: mockDOMFactory } ); const framesHTML = frames.map((frame) => frame.innerHTML).join(""); expect(framesHTML).to.eql(expectationSanityCheck); expect(framesHTML).to.eql( baselineFrames.map((frame) => frame.innerHTML).join("") ); } }); }); }); ================================================ FILE: packages/paperclip-web-renderer/src/test/frame-test.ts ================================================ import { patchFrame, patchFrames, renderFrame, renderFrames } from ".."; import { createMockEngine } from "@paperclip-ui/core/lib/test/utils"; import { mockDOMFactory } from "./utils"; import { expect } from "chai"; import { LoadedPCData } from "@paperclip-ui/utils"; describe(__filename + "#", () => { it(`Can render simple frames`, () => { const engine = createMockEngine({ "hello.pc": `
    Hello world
    `, }); const frames = renderFrames(engine.open("hello.pc"), { domFactory: mockDOMFactory, }); expect(frames.map((frame) => frame.innerHTML).join("")).to.eql( `
    Hello world
    ` ); }); it(`Can render frames with imported CSS`, () => { const engine = createMockEngine({ "hello.pc": `
    Hello world
    `, "/imp.css": `div {color: blue;}`, }); const frames = renderFrames(engine.open("hello.pc"), { domFactory: mockDOMFactory, }); expect(frames.map((frame) => frame.innerHTML).join("")).to.eql( `
    Hello world
    ` ); }); it(`Can a render a slot`, () => { const engine = createMockEngine({ "hello.pc": `
    {child}
    `, }); const frames = renderFrames(engine.open("hello.pc"), { domFactory: mockDOMFactory, showSlotPlaceholders: true, }); expect(frames.map((frame) => frame.innerHTML).join("")).to.eql( `
    ` ); }); it(`Hides slots by default`, () => { const engine = createMockEngine({ "hello.pc": `
    {child}
    `, }); const frames = renderFrames(engine.open("hello.pc"), { domFactory: mockDOMFactory, showSlotPlaceholders: false, }); expect(frames.map((frame) => frame.innerHTML).join("")).to.eql( `
    ` ); }); it(`Can render a single frame`, () => { const engine = createMockEngine({ "hello.pc": `a`, }); const frame = renderFrame(engine.open("hello.pc"), 0, { domFactory: mockDOMFactory, }); expect(frame.innerHTML).to.eql( `
    a
    ` ); }); it(`Can patch a single frame`, () => { const engine = createMockEngine({ "hello.pc": `a`, }); const content = engine.open("hello.pc"); const frame = renderFrame(content, 0, { domFactory: mockDOMFactory, }); engine.updateVirtualFileContent("hello.pc", "bbb"); const newContent = engine.open("hello.pc"); patchFrame(frame, 0, content, newContent, { domFactory: mockDOMFactory }); expect(frame.innerHTML).to.eql( `
    bbb
    ` ); }); it(`Properly renders with a protocol`, async () => { const graph = { "/entry.pc": ` `, "/file.jpg": `a`, "/something-else.jpg": `a`, }; const engine = createMockEngine(graph); const resolveUrl = (url) => "blah://" + url; const frames = renderFrames(engine.open("/entry.pc") as any, { domFactory: mockDOMFactory, resolveUrl, }); expect(frames.map((frame) => frame.innerHTML).join("")).to.eql( `
    ` ); const prevData = engine.open("/entry.pc") as any; engine.updateVirtualFileContent( "/entry.pc", ` ` ); patchFrames(frames, prevData, engine.open("/entry.pc") as any, { domFactory: mockDOMFactory, resolveUrl, }); expect(frames.map((frame) => frame.innerHTML).join("")).to.eql( `
    ` ); }); [ [ `Can replace a frame`, { "hello.pc": "
    ", }, { "hello.pc": "blah", }, ], [ `Replaces a node if the tag name doesn't match`, { "hello.pc": "
    ", }, { "hello.pc": "", }, ], [ `Adds a frame`, { "hello.pc": "a", }, { "hello.pc": "a", }, ], [ `Removes a frame`, { "hello.pc": "a", }, { "hello.pc": "", }, ], [ `Adds a child`, { "hello.pc": "", }, { "hello.pc": "a", }, ], [ `Removes a child`, { "hello.pc": "a", }, { "hello.pc": "", }, ], [ `Can change text value`, { "hello.pc": "a", }, { "hello.pc": "b", }, ], [ `Can change style`, { "hello.pc": "a", }, { "hello.pc": "a", }, ], [ `Can insert a style`, { "hello.pc": "a", }, { "hello.pc": "a", }, ], [ `Can remove a style`, { "hello.pc": "a", }, { "hello.pc": "a", }, ], [ `Can add style in import`, { "hello.pc": "a", "imp.pc": "", }, { "hello.pc": "a", "imp.pc": "", }, ], [ `Can add an import`, { "hello.pc": "a", "imp.pc": "", "imp2.pc": "", }, { "hello.pc": "a", "imp2.pc": "", }, ], [ `Can remove an import`, { "hello.pc": "a", "imp.pc": "", "imp2.pc": "", }, { "hello.pc": "a", }, ], [ `Can remove an in an import`, { "hello.pc": "a", "imp.pc": "", "imp2.pc": "", "imp3.pc": "", }, { "imp2.pc": "", }, ], [ `Can add an attribute`, { "hello.pc": `
    `, }, { "hello.pc": `
    `, }, ], [ `Can remove an attribute`, { "hello.pc": `
    `, }, { "hello.pc": `
    `, }, ], [ `can patch a slot`, { "hello.pc": `
    {child}
    `, }, { "hello.pc": `
    {child}
    `, }, ], ].forEach(([title, ...graphs]: any) => { it(title, () => { const engine = createMockEngine(graphs[0]); const data = engine.open("hello.pc"); let frames = renderFrames(data, { domFactory: mockDOMFactory }); for (let i = 1; i < graphs.length; i++) { const graph = graphs[i]; for (const name in graph) { engine.updateVirtualFileContent(name, graph[name]); } const newData = engine.open("hello.pc"); frames = patchFrames(frames, data, newData, { domFactory: mockDOMFactory, }); const newFrames = renderFrames(newData, { domFactory: mockDOMFactory }); expect(frames.map((frame) => frame.innerHTML).join("")).to.eql( newFrames.map((frame) => frame.innerHTML).join("") ); } }); }); }); ================================================ FILE: packages/paperclip-web-renderer/src/test/fuzzy-test.ts ================================================ import { generateRandomPaperclipDocument } from "./random"; import { expect } from "chai"; import { createMockEngine, mockDOMFactory } from "./utils"; import { repeat } from "lodash"; import { patchFrame, patchFrames, renderFrame, renderFrames } from ".."; import { LoadedPCData } from "@paperclip-ui/utils"; describe(__filename + "#", () => { xit("passes the fuzzy test", async () => { const randOptions = { minWidth: 1, maxWidth: 4, minDepth: 1, maxDepth: 4, }; let currentDocumentSource = generateRandomPaperclipDocument(randOptions); const graph = { "/entry.pc": currentDocumentSource, }; const engine = await createMockEngine(graph); let frames = renderFrames(engine.open("/entry.pc") as LoadedPCData, { domFactory: mockDOMFactory, }); for (let i = 50; i--; ) { const randomDocument = generateRandomPaperclipDocument(randOptions); // too lazy to fix if (!randomDocument.trim()) { continue; } const baselineEngine = await createMockEngine({ "/entry.pc": randomDocument, }); try { const baselineFrames = renderFrames( baselineEngine.open("/entry.pc") as LoadedPCData, { domFactory: mockDOMFactory } ); const oldData = engine.getLoadedData("/entry.pc") as LoadedPCData; await engine.updateVirtualFileContent("/entry.pc", randomDocument); frames = patchFrames( frames, oldData, engine.getLoadedData("/entry.pc") as LoadedPCData, { domFactory: mockDOMFactory } ); for (let i = 0, { length } = frames; i < length; i++) { const frameA = frames[i]; const frameB = baselineFrames[i]; expect(frameA.innerHTML.replace(/[\n\s]/g, " ")).to.eql( frameB.innerHTML.replace(/[\n\s]/g, " ") ); } currentDocumentSource = randomDocument; } catch (e) { console.error(`Fuzzy test failed to diff & patch:\n`); console.error(repeat("-", 80)); console.error(currentDocumentSource); console.error(repeat("-", 80)); console.error(repeat("-", 37) + " into " + repeat("-", 37)); console.error(repeat("-", 80)); console.error(randomDocument); console.error(repeat("-", 80)); throw e; } } }); }); ================================================ FILE: packages/paperclip-web-renderer/src/test/random/document.ts ================================================ import { generateNode } from "./html"; import { Options, Context, randomArray, pickRandom } from "./utils"; import { generateStyleElement } from "./styles"; // TODO - include component definitions & imports here export const generateRandomPaperclipDocument = (options: Options) => { let buffer = ""; const context: Context = { depth: 0 }; buffer += randomArray(options.minWidth, options.maxWidth) .map(() => { return pickRandom([generateNode, generateStyleElement], [4, 1])( options, context ); }) .join("\n"); return buffer; }; ================================================ FILE: packages/paperclip-web-renderer/src/test/random/html.ts ================================================ // TODO - move this to @paperclip-ui/utils import { clamp, random } from "lodash"; import { randomChars, pickRandom, randomArray, Options, Context } from "./utils"; export const generateRandomPaperclipDocument = (options: Options) => { let buffer = ""; const context: Context = { depth: 0 }; buffer += randomArray(options.minWidth, options.maxWidth) .map(() => { return generateNode(options, context); }) .join("\n"); }; export const generateNode = (options: Options, context: Context) => { return pickRandom( [generateTextNode, generateFragment, generateBinding, generateElement], [1, 1, 1, 3] )(options, context); }; const generateTextNode = (options: Options, context: Context) => { return randomChars(0, 20); }; const generateFragment = (options: Options, context: Context) => { return `${generateChildNodes(options, context)}`; }; const generateElement = (options: Options, context: Context) => { let tagName; while (1) { tagName = randomChars(1, 3, 3); // Probably never going to happen, but no harm in checking if (!/img|br|import/.test(tagName)) { break; } } let buffer = `<` + tagName; buffer += randomArray(options.minWidth, options.maxWidth) .map(() => { return " " + generateAttribute(options, context); }) .join(""); if (pickRandom([true, false], [3, 1])) { buffer += ">"; buffer += generateChildNodes(options, context); return buffer + ``; } else { // play with whitespace at end return buffer + pickRandom([" />", "/>", "/>"]); } }; const generateChildNodes = (options: Options, context: Context) => { if (context.depth >= options.maxDepth) { return ""; } let buffer = ""; if (context.depth < options.maxDepth) { buffer += generateNode(options, { ...context, depth: context.depth + 1 }) + "\n"; } return buffer; }; const generateAttribute = (options: Options, context: Context) => { return pickRandom([generateKeyValueAttribute, generateBinding], [2, 1])( options, context ); }; const generateKeyValueAttribute = (options: Options, context: Context) => { const buffer = randomChars(1, 3, 3); if (pickRandom([true, false], [2, 1])) { return buffer + "=" + generateAttributeValue(options, context); } else { return buffer; } }; const generateAttributeValue = (options: Options, context: Context) => { return pickRandom([ generateAttributeStringValue, generateAttributeDynamicStringValue, generateBinding ])(options, context); }; const generateAttributeStringValue = (options: Options, context: Context) => { return `"${randomChars(0, 3, 3)}"`; }; const generateAttributeDynamicStringValue = ( options: Options, context: Context ) => { let buffer = `"`; buffer += randomArray(options.minWidth, options.maxWidth) .map(() => { return pickRandom([ randomChars(1, 3), `{${randomChars(1, 3)}}`, `{${randomChars(1, 3)}?}` // optional ]); }) .join(" "); return buffer + `"`; }; const generateBinding = (options: Options, context: Context) => { return pickRandom([ `{${randomChars(1, 3)}}`, `{${randomChars(1, 3)}?}` // optional ]); }; ================================================ FILE: packages/paperclip-web-renderer/src/test/random/index.ts ================================================ export * from "./document"; ================================================ FILE: packages/paperclip-web-renderer/src/test/random/styles.ts ================================================ import { randomChars, Options, Context, randomArray, pickRandom } from "./utils"; export const generateStyleElement = (options: Options, context: Context) => { let buffer = ``; }; const generateCSSRule = (options: Options, context: Context) => { return pickRandom([generateCSSStyleRule, generateCSSMediaRule])( options, context ); }; const generateCSSMediaRule = (options: Options, context: Context) => { return ( `@media ${randomChars(1, 5, 5)} {\n` + ` ${randomArray(options.minWidth, options.maxWidth) .map(() => generateCSSStyleRule(options, { ...context, depth: context.depth + 1 }) ) .join("\n")}` + `}\n\n` ); }; const generateCSSStyleRule = (options: Options, context: Context) => { return ( `.${randomChars(1, 5, 5)} {\n` + ` ${randomArray(options.minWidth, options.maxWidth) .map(() => { return pickRandom( context.depth < options.maxDepth ? [generateCSSSDeclaration, generateCSSStyleRule] : [generateCSSSDeclaration] )(options, { ...context, depth: context.depth + 1 }); }) .join("\n")}` + `}\n\n` ); }; const generateCSSSDeclaration = (options: Options, context: Context) => { // don't want to be totally random return `${randomChars(1, 4, 3)}: ${randomChars(1, 4)};`; }; randomChars; ================================================ FILE: packages/paperclip-web-renderer/src/test/random/utils.ts ================================================ import { clamp } from "lodash"; const AZRANGE = 123 - 97; export type Options = { minDepth: number; maxDepth: number; minWidth: number; maxWidth: number; }; export type Context = { depth: number; }; const A2Z = Array.from({ length: AZRANGE }).map((v, i) => String.fromCharCode(i + 97) ); export const randomChars = ( min: number, max: number, range: number = AZRANGE ) => { const azOptions = A2Z.slice(0, range); return randomArray(min, max) .map(() => pickRandom(azOptions)) .join(""); }; export const randomArray = (min: number, max: number) => Array.from({ length: clamp(Math.round(Math.random() * max), min, max) }); export const pickRandom = (options: any[], weights?: any[]) => { if (!weights) { weights = Array.from({ length: options.length }).map(() => 1); } const weightedOptions = options.reduce((weightedOptions, option, index) => { return [ ...weightedOptions, ...Array.from({ length: weights[index] }).map(() => { return option; }) ]; }, []); return weightedOptions[Math.floor(Math.random() * weightedOptions.length)]; }; ================================================ FILE: packages/paperclip-web-renderer/src/test/utils.ts ================================================ import { DOMFactory } from "../renderer"; import { createEngineDelegate } from "@paperclip-ui/core"; import * as path from "path"; import { FramesRenderer } from "../frame-renderer"; import { EngineMode } from "@paperclip-ui/core"; import { identity } from "lodash"; import * as CSSOM from "cssom"; import { FramesRendererState } from ".."; export const mockDOMFactory: DOMFactory = { createElement: (tagName) => { if (tagName === "style") { return new StyleElement() as any as HTMLElement; } return new MockElement(tagName) as any as HTMLElement; }, createElementNS: (tagName) => new MockElement(tagName) as any as HTMLElement, createDocumentFragment: () => new MockFragment() as any as DocumentFragment, createTextNode: (nodeValue) => new MockTextNode(nodeValue) as any as Text, }; export const combineFrameHTML = (state: FramesRendererState) => { return state.frames.map((frame) => frame.stage.innerHTML).join(""); }; export const combineFrameHTML2 = (frames: HTMLElement[]) => { return frames.map((frame) => frame.innerHTML).join(""); }; abstract class BaseNode { $$parent: ParentNode; remove() { this.$$parent.removeChild(this); } get parentNode() { return this.$$parent; } get innerHTML() { return this.getInnerHTML(); } abstract cloneNode(); abstract getInnerHTML(); abstract toString(); } abstract class ParentNode extends BaseNode { childNodes: BaseNode[] = []; appendChild(child: BaseNode) { child.$$parent = this; if (child instanceof MockFragment) { child.childNodes.forEach((child) => { child.$$parent = this; }); this.childNodes.push(...child.childNodes); } else { this.childNodes.push(child); } } get lastChild() { return this.childNodes[this.childNodes.length - 1]; } removeChild(child: BaseNode) { child.$$parent = null; this.childNodes.splice(this.childNodes.indexOf(child), 1); } insertBefore(child: BaseNode, ref: BaseNode) { const index = this.childNodes.indexOf(ref); if (index === -1) { throw new Error(`ref not found`); } const children = child instanceof MockFragment ? child.childNodes : [child]; for (const child of children) { child.$$parent = this; } this.childNodes.splice(index, 0, ...children); } getInnerHTML() { let buffer = ""; for (const child of this.childNodes) { buffer += child.toString(); } return buffer; } } class StyleElement extends ParentNode { private _textContent: string; private _sheet: CSSOM.StyleSheet; private _attrs: Record = {}; get textContent() { return this._textContent; } set textContent(value: string) { this._textContent = value; this._sheet = CSSOM.parse(value); } get sheet() { return this._sheet; } setAttribute(name, value) { this._attrs[name] = value; } getAttribute(name, value) { return this._attrs[name]; } cloneNode() { const el = new StyleElement(); el.textContent = this.sheet.toString(); return el; } toString() { return ``; } } class MockElement extends ParentNode { attributes = {}; style = {}; textContent = ""; constructor(readonly tagName: string) { super(); } setAttribute(name: string, value: string) { this.attributes[name] = value; } cloneNode() { const el = new MockElement(this.tagName); for (const key in this.attributes) { el.setAttribute(key, el.attributes[key]); } el.textContent = this.textContent; for (const child of this.childNodes) { el.appendChild(child.cloneNode()); } return el; } // eslint-disable-next-line addEventListener() {} removeAttribute(name: string) { delete this.attributes[name]; } toString() { let buffer = `<${this.tagName}`; const sortedAttributes = Object.keys(this.attributes) .sort() .map((name) => ({ name, value: this.attributes[name] })); for (const { name, value } of sortedAttributes) { if (!value) { continue; } buffer += ` ${name}=${JSON.stringify(value)}`; } buffer += `>`; buffer += this.getInnerHTML() || this.textContent.replace(/[\s\r\n\t]+/g, " "); buffer += ``; return buffer; } } class MockFragment extends ParentNode { cloneNode() { const clone = new MockFragment(); for (const child of this.childNodes) { clone.appendChild(child.cloneNode()); } return clone; } toString() { return ""; } } class MockTextNode extends BaseNode { constructor(public nodeValue: string) { super(); } cloneNode() { return new MockTextNode(this.nodeValue); } getInnerHTML() { return this.toString(); } toString() { return this.nodeValue; } } export type Graph = { [identifier: string]: string; }; export const createMockEngine = (graph: Graph) => createEngineDelegate({ io: { readFile: (uri) => graph[uri.replace("file://", "")] || graph[uri.replace(/\\+/g, "/")], fileExists: (uri) => Boolean( graph[uri.replace("file://", "")] || graph[uri.replace(/\\+/g, "/")] ), resolveFile: (from, to) => { const prefix = from.indexOf("file:") === 0 ? "file://" : ""; return ( prefix + path .join(path.dirname(from.replace("file://", "")), to) .replace(/\\+/g, "/") ); }, }, mode: EngineMode.MultiFrame, }); export const createMockEngineDelegate = ( graph: Graph, mode: EngineMode = EngineMode.SingleFrame ) => createEngineDelegate({ io: { readFile: (uri) => graph[uri.replace("file://", "")] || graph[uri.replace(/\\+/g, "/")], fileExists: (uri) => { return Boolean( graph[uri.replace("file://", "")] || graph[uri.replace(/\\+/g, "/")] ); }, resolveFile: (from, to) => { const prefix = from.indexOf("file:") === 0 ? "file://" : ""; return ( prefix + path .join(path.dirname(from.replace("file://", "")), to) .replace(/\\+/g, "/") ); }, }, mode, }); export const createMockFramesRenderer = ( uri = "", resolveUrl: (url: string) => string = identity ) => new FramesRenderer(uri, resolveUrl, mockDOMFactory, noop); // eslint-disable-next-line const noop = () => {}; export const trimWS = (str: string) => str.replace(/[\s\r\n\t]+/g, " "); ================================================ FILE: packages/paperclip-web-renderer/src/utils/index.ts ================================================ export * from "./misc"; ================================================ FILE: packages/paperclip-web-renderer/src/utils/misc.ts ================================================ import { computeVirtScriptObject, memoize, NodeAnnotations, VirtualFrame, } from "@paperclip-ui/utils"; export const preventDefault = (event: any) => { event.stopPropagation(); event.preventDefault(); return false; }; export const ATTR_ALIASES = { className: "class", }; export const traverseNativeNode = ( element: Node, each: (node: Node, path: number[]) => void, cpath: number[] = [] ) => { each(element, cpath); for (let i = 0, { length } = element.childNodes; i < length; i++) { const child = element.childNodes[i]; traverseNativeNode(child, each, [...cpath, i]); } }; export const arraySplice = >( array: TArray, index: number, deleteCount: number, ...newValues: TArray ) => { return [ ...array.slice(0, index), ...newValues, ...array.slice(index + deleteCount), ]; }; export const getFrameBounds = memoize((node: VirtualFrame) => { const annotations: NodeAnnotations = (node.annotations && computeVirtScriptObject(node.annotations)) || {}; return { width: 1024, height: 768, x: 0, y: 0, ...(annotations.frame || {}), }; }); ================================================ FILE: packages/paperclip-web-renderer/tsconfig.json ================================================ { "compilerOptions": { "outDir": "lib", "target": "es5", "moduleResolution": "node", "declaration": true, "module": "commonjs", "allowSyntheticDefaultImports": true }, "exclude": ["node_modules", "*-test", "lib", "index.d.ts", "test"], "filesGlob": ["./src/**/*.ts"] } ================================================ FILE: packages/paperclip-web-renderer/uis/loader.pc ================================================
    ================================================ FILE: packages/paperclip-web-renderer/uis/paperclip.config.json ================================================ { "srcDir": "./src" } ================================================ FILE: packages/paperclip-web-renderer/webpack.config.js ================================================ const path = require("path"); const webpack = require("webpack"); /* * SplitChunksPlugin is enabled by default and replaced * deprecated CommonsChunkPlugin. It automatically identifies modules which * should be splitted of chunk by heuristics using module duplication count and * module category (i. e. node_modules). And splits the chunks… * * It is safe to remove "splitChunks" from the generated configuration * and was added as an educational example. * * https://webpack.js.org/plugins/split-chunks-plugin/ * */ const HtmlWebpackPlugin = require("html-webpack-plugin"); /* * We've enabled HtmlWebpackPlugin for you! This generates a html * page for you when you compile webpack, which will make you start * developing and prototyping faster. * * https://github.com/jantimon/html-webpack-plugin * */ const mode = process.env.NODE_ENV === "production" ? "production" : "development"; module.exports = { mode, entry: "./src/browser.ts", output: { filename: "browser.js", path: path.resolve(__dirname, "dist") }, externals: { chokidar: "{}" }, plugins: [new webpack.ProgressPlugin(), new HtmlWebpackPlugin()], module: { rules: [ { test: /.(ts|tsx)?$/, loader: "ts-loader", include: [path.resolve(__dirname, "src")], exclude: [/node_modules/] } ] }, optimization: { splitChunks: { cacheGroups: { vendors: { priority: -10, test: /[\\/]node_modules[\\/]/ } }, chunks: "async", minChunks: 1, minSize: 30000, name: true } }, devServer: { open: true }, resolve: { extensions: [".tsx", ".ts", ".js"] } }; ================================================ FILE: packages/paperclip-website/.gitignore ================================================ .docusaurus build ================================================ FILE: packages/paperclip-website/README.md ================================================ # Website This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. ### Installation ``` $ yarn ``` ### Local Development ``` $ yarn start ``` This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. ### Build ``` $ yarn build ``` This command generates static content into the `build` directory and can be served using any static contents hosting service. ### Deployment ``` $ GIT_USER= USE_SSH=true yarn deploy ``` If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. ================================================ FILE: packages/paperclip-website/babel.config.js ================================================ module.exports = { presets: [require.resolve("@docusaurus/core/lib/babel/preset")], sourceType: "unambiguous" }; ================================================ FILE: packages/paperclip-website/blog/2020-03-01-introducing-paperclip.md ================================================ --- title: Introducing Paperclip description: Introducing Paperclip, a DSL for presentational components slug: introducing-paperclip authors: - name: Craig Condon title: Creator of Paperclip url: https://github.com/crcn image_url: https://github.com/crcn.png image: https://i.imgur.com/mErPwqL.png hide_table_of_contents: false --- I’ve been building front-end applications for a while now, and one of my biggest gripes is just how slow and un-fun it is. If anyone were to ask me what the biggest time sink in web development is, I’d say that it’s writing HTML and CSS. That’s not what I want to spend my time on! ![Slow HMR Demo](./assets/hmr-demo.gif) HTML & CSS development is mostly visual, yet, developers write it by hand, then have to wait a few seconds to see their UI appear in the browser. That’s not so bad for small projects, but larger projects that take 3+ seconds to reload can be excruciating to work with. Not only that, but this bottleneck can also affect the quality of the app. I’ve worked with many designers and developers over the years, and the consistent pattern I’ve seen with every team is that developers would often-times either cut design corners in order to ship features on time, or give large estimates so that they could spend 90% of their time making sure that the UI is perfect. To pile on to that, I’ve found that developers typically wouldn’t translate designs correctly, and UIs would be just a little off. Since designers can’t really make changes themselves, they usually ask their developer to fix the problem who then points them to “file a bug in GitHub” in the endless sea of tickets, never to be seen again. This is a problem that I wanted to fix. I starting focusing on UI builders four years ago, there should be a better tool for creating web UIs that has an experience similar to Figma or Sketch. Three years later and after many failed ideas, I finally feel like I built something that’s practical enough to use. ## A hybrid tool between design & code Paperclip is a free and open source DSL for presentational components that brings web development closer to a designer-like experience, all within your existing IDE. Here’s the look and feel of it: ![Realtime preview](./assets/realtime-preview-1.gif) You never have to leave your editor to create UIs, or wait around for changes to appear. Previews update instantly as you’re making changes, even for very large projects. ![Large projects](./assets/preview-large-projects.gif) > If you’re not using one of the supported IDEs, you can run the command line tool instead to launch the standalone preview app. From there you can use any text editor you want. Paperclip just covers your application’s appearance, and the tooling is designed to help you build your UI out as quickly as possible. The syntax is simple, and just covers HTML, CSS, and primitive components. Here’s an example: ```html
    Hello {name}!
    ``` It’s pure UI development focus without logic and other stuff getting in the way. After you’ve defined your Paperclip UIs, you can import them into into your app. ## How to use Paperclip in your app > Paperclip is designed to support many different languages and frameworks. For the Beta however, it only supports React, and TypeScript apps. Paperclip UIs compile to plain code by either using the command line tool, or by using the Webpack loader for a cleaner integration. From there you can just import a UI file like a normal module: ```javascript import * as styles from "./styles.pc"; function MyApp() { return ; } ``` If you’re familiar with Styled Components and other CSS-in-JS libraries, you’ll find that the API is similar. Just like CSS-in-JS libraries, styles are isolated to the components they’re assigned to, so you don’t have to worry about CSS leaking out to the rest of your app. Here’s a more complete example of a design file: ```html
    {children}
    {subtitle}
    {children}
    ``` > This is a snippet taken from production code, and isn’t as clean as it could be since there are some CSS properties that should be derived from design tokens (such as margin-top: 46px, font-family: Eina03). However, this isn’t that big of a problem with Paperclip since this file is covered for visual regressions (more on that later). We can confidently refactor this later without changing what the UI looks like. Here’s some of the React code associated with this design file above: ```javascript import * as styles from "./styles.pc"; const InvestorSignupPage = () => { const { title, accountKinds, onAccountKindChange, onContinue, onSignup, } = useSignupPage(); return ( Log in} /> Choose an account onAccountKindChange(AccountKind.PortfolioAnalytics)} /> onAccountKindChange(AccountKind.CreditInvesting)} /> ); }; ``` ![screenshot](./assets/investor-signup-demo.png) > With Paperclip, you can create just about any web UI you want with few restrictions. You can also use as much or as little of it as you want. ## Perfect for your design system Paperclip is a great tool for your design system since it gives you a fast and free-form environment for creating building blocks that you can re-use throughout your application. Here’s an example of a design system that I’m currently working on at the time of writing this article: ![screenshot](./assets/ds-screenshot.png) You can build your design system out quickly since every style change appears instantly across your entire app. For finicky things such as layout, this is a very useful feature to have. If you prefer to keep your UIs organized in separate files, you can do that and your UIs will be just as searchable by developers looking for a particular component. ![screenshot](./assets/grid-view.gif) Paperclip is not only a great tool for your design system because it’s a fast environment, there’s also a great amount of safety that Paperclip offers to help you build scalable and maintainable UIs that are resilient to visual production bugs and software rot. ## Explicit CSS One of the problems I find with CSS is the global nature of it. It can be manageable for small projects, but can quickly become unwieldy when your app and team gets larger. Typically what happens I’ve found is that with global CSS, it becomes unknown about what parts of the app styles are applied to. As a result of that, engineers can feel comfortable about making CSS changes and decide that the safest route is to leave it alone (or risk shipping a bug to production). Over the course of years as CSS piles up, it’s easy for the whole styling system to become a giant Rube Goldberg machine. That tech debt never usually goes away, developers just deal with it. Today, developers have systems in place such as CSS Modules, CSS-in-JS, SMACSS, and such to help manage some of the issues around CSS. However, most of the tooling still allows developers to easily write global CSS in some way (like nested style selectors which has caused visual bugs in my experience). In Paperclip, that risk is greatly diminished. In Paperclip, CSS is scoped in the document it’s defined in. Here’s an example: ```html
    I'm red! I'm purple!
    ``` If you want to stylize another document, you need to be explicit about it. Here’s an example: ```html Craig ``` In Message.pc, all you need to do is define labelClass on the element that you would like to allow style overrides: ```html
    Hello {children}!
    ``` This allows you to be clear about your module boundaries since you know exactly what can and can’t be styled by other modules. This also goes the other way around for modules that you’d like to use styles from, which is great for third-party CSS. For example, here’s how you might use Tailwind with Paperclip: ```html

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

    sit voluptatem
    ``` With this approach, you know exactly how third-party CSS is used, and don’t have to worry about it accidentally styling other parts of your app. Of course, you can always use global CSS if you want to (and you may need it for certain edge cases), but you must be explicit about that. For example: ```html ``` This way there’s no guessing about what is and isn’t global. ## Building UIs more accurately Building web UIs can be a finicky thing, especially when you’re considering things like box model, borders, and other layout properties that can change the size and spacing of your elements. Paperclip comes with measuring tools and other inspection utilities to help make it easier to match your UIs with your design. ![screenshot](./assets/measing.gif) ## Extensive visual bug protection Every frame that is defined within Paperclip is automatically covered for visual bugs. All you need to do is run Paperclip’s visual regression tool against your project, and you’ll have protection against visual issues in production. It’s really that easy. ![screenshot](./assets/percy.gif) Why is this awesome? It means that you can be more confident about making big sweeping style changes across your app. Reskinning your app? No problem. Don’t know what that CSS does? Delete it, you’re covered. Responsive testing is also easy to set up. Just create a new frame, resize it to whatever width you want, and you’re done. If you have the visual regression tool set up, it will automatically capture this new frame and provide you with protection. ## Where’s Paperclip heading? Eventually there will be more visual tooling for things like CSS grid, spacing, and other CSS features that make sense to edit visually. My goal is to eventually build a tool that finds a good balance between coding and design. I’m also aiming to build more tooling that makes product development more accessible to designers, giving them control over the actual UI. It’s my sense that designers need to be in control since designs that are translated by developers are almost always a little off. Designer tooling could be in the form of a tool that synchronizes Figma designs to Paperclip sources (via Zeplin or Avocode), or maybe designers will feel comfortable enough to use a UI builder that writes Paperclip files. Maybe both options. And with safety tools such as visual regression testing, I believe that designers will be able to confidently ship features without needing the assistance of developers (probably just for code reviews). For now though, Paperclip will remain developer-focused. Once the foundation is solid, Paperclip will move onto becoming a more accessible tool for designers and other non-coders who want to help build production-ready UIs. If you’re interested in trying out Paperclip, let me know! ================================================ FILE: packages/paperclip-website/docs/configure-jest.md ================================================ --- id: configure-jest title: Configure Paperclip with Jest sidebar_label: Jest --- You can include Paperclip UIs directly in your Jest tests. ## Installation ```sh npm install jest-paperclip --save-dev ``` ## package.json Config After installing the `jest-paperclip` module, You'll need to update the `jest.transform` property like so: ```json "jest": { "transform": { "^.+\\.pc$": "jest-paperclip" } } ``` After that, you should be able to import `*.pc` files in Jest tests. ================================================ FILE: packages/paperclip-website/docs/configure-paperclip.md ================================================ --- id: configure-paperclip title: Configuration sidebar_label: Configuring --- The `paperclip.config.json` contains information about linting rules, compiler options, and such. Here are all of the options you can use: ```typescript type PaperclipConfig = { // source directory where *.pc files live srcDir?: string; // directories where modules are stored moduleDirs?: string[]; // options for the output settings compilerOptions?: CompilerOptions | CompilerOptions[]; lintOptions?: LintOptions; }; type CompilerOptions = { // target compiler to use. Default is all of the ones installed. target?: string; // Files for the target compiler to emit. E.g: [d.ts, js, css] emit?: string[]; // where PC files should be compiled to. If undefined, then // srcDir is used. outDir?: string; // treat assets as modules. This is particularly useful for bundlers. importAssetsAsModules?: boolean; // Combine all CSS into this one file. If unspecified, then CSS files are generated // for each PC file mainCSSFileName?: string; // embed assets until this size. If -1, then there is no limit embedAssetMaxSize?: number; // output directory for non-PC files. If not specified, then srcDir // will be used assetOutDir?: string; // prefix for assets, assetPrefix?: string; useAssetHashNames?: boolean; }; type LintOptions = { // flag CSS code that is not currently used noUnusedStyles?: boolean; // enforce CSS vars for these properties enforceVars?: string[]; }; ``` ### Basic example At the bare minimum, it's recommended that you specify a `srcDir` like so: ```json { "srcDir": "./src" } ``` By default, files will be emitted to the same directory as your `*.pc` files. If you want, you can emit files to another directory like so: ```json { "compilerOptions": { "outDir": "./lib" }, "srcDir": "./src" } ``` ### Expanded example Here's a bit more of an expanded example: ```json { "srcDir": "./src", "moduleDirs": ["node_modules"], "compilerOptions": { "target": "react", // Only emit these files "emit": ["js", "d.ts", "css"], // compile files to this directory "outDir": "./lib", // Emit all assets (png, svg, css) to this directory "assetOutDir": "./lib/assets" // combine all CSS into this file "mainCSSFileName": "main.css", // embed all assets "embedAssetMaxSize": -1, "assetPrefix": "https://my-cdn.com", "useAssetHashNames": true, }, "lintOptions": { "noUnusedStyles": true, "enforceVars": [ "font-family", "color" ] } } ``` ### Compiling to multiple directories You may also specify **multiple** compiler targets within a Paperclip config file. For example: ```json { "compilerOptions": [ { "target": "react", "outDir": "./lib/react" }, { "target": "html", "outDir": "./lib/html" } ], "srcDir": "./src" } ``` If you're using TypeScript, you can leverage this behavior to generate typed definition files in your source directory. For example: ```json { "compilerOptions": [ // Emit compiled JS to the lib directory { "target": "react", "outDir": "./lib" }, // // Emit d.ts in the source directory { "target": "react", "emit": ["d.ts"] } ], "srcDir": "./src" } ``` ================================================ FILE: packages/paperclip-website/docs/configure-percy.md ================================================ --- id: configure-percy title: Setting Up Visual Regression Tests sidebar_label: Percy --- ## Installation Paperclip integrates with [Percy](https://percy.io) to allow you test for CSS bugs in your Paperclip UI files. To get started, install the NPM module: ```sh npm install percy-paperclip --save-dev ``` Next, grab your percy token, then run the following command in the same directory as your `paperclip.config.json` file: ```sh PERCY_TOKEN=[TOKEN] yarn percy-paperclip ``` After that, you should see something like this: ![Percy demo](/img/snapshot.gif) That's it! You're now set up to catch visual regressions in your UIs. ## Setting up with GitHub actions `percy-paperclip` pairs nicely with GitHub actions, especially for PR checks. Here's a GitHub action you can use: ```yml name: PR Checks on: pull_request jobs: visual-regression-test: name: Visual Regression Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 - uses: actions/checkout@v2 with: fetch-depth: 0 # fetches all branches - name: Maybe snapshot run: | CHANGED_PC_FILES=$(git diff --name-only origin/${{ github.base_ref }} origin/${{ github.head_ref }} -- "./**/*.pc") if [ -n "$CHANGED_PC_FILES" ]; then yarn add percy percy-paperclip percy exec -- percy-paperclip fi working-directory: ./path/to/frontend env: PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} ``` > Be sure to change `working-directory` to point to where your `paperclip.config.json` file is. ☝🏻 This script will run only when PC files change, so if you're working with people working on the back-end, for instance, they won't get this check (since we're assuming they won't touch PC files). To go along with the script above, you'll need to set up a [baseline](https://docs.percy.io/docs/baseline-picking-logic) for your master branch. Here's a script for that: ```yml name: Master Checks on: push: branches: - master jobs: visual-regression-test: name: Visual Regression Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Maybe snapshot run: | CHANGED_PC_FILES=$(git diff --name-only origin/master^ origin/master -- "./**/*.pc") if [ -n "$CHANGED_PC_FILES" ]; then yarn add percy yarn snapshot fi working-directory: ./path/to/frontend env: PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} ``` > Again, be sure to change `working-directory` to point to where your `paperclip.config.json` file is. ☝🏻 This script runs whenever a `*.pc` file changes on master, and ensures that subsequent PRs are visually testing against the correct baseline. ================================================ FILE: packages/paperclip-website/docs/configure-prettier.md ================================================ --- id: configure-prettier title: Prettier Usage sidebar_label: Prettier --- Assuming that you have [Prettier](https://prettier.io/) installed, just run: ``` yarn add prettier-plugin-paperclip --dev ``` After that you should be good to go! Just be sure that prettier is configured to pick up `*.pc`. ================================================ FILE: packages/paperclip-website/docs/configure-webpack.md ================================================ --- id: configure-webpack title: Setting Up Webpack sidebar_label: Webpack --- > Take a look at the [TODO MVC example](https://github.com/paperclipui/paperclip/tree/master/examples/react-todomvc) to see how everything is put together. You can use Paperclip with [Webpack](https://webpack.js.org/) by installing the loader: ```sh npm install paperclip-loader --save-dev ``` Also, be sure that you also have the following dependencies installed (If you're using [NextJS](https://nextjs.org/), then you can skip this step): ```sh npm install style-loader css-loader file-loader --save-dev ``` > Paperclip emits CSS files that need to be loaded, so this is why you need to install additional dependencies. After that, you can can include `paperclip-loader` in your webpack config rules: ```javascript { test: /\.pc$/, loader: "paperclip-loader", options: { // paperclip.config.json can be generated via the @paperclip-ui/cli tool config: require("./paperclip.config.json") } } ``` > ☝ be sure that you have a [paperclip.config.json](/docs/configure-paperclip) file. For context, here's what your entire Webpack config might look like: ```javascript const path = require("path"); const webpack = require("webpack"); module.exports = { entry: "./src/index.js", output: { filename: "[name].js", path: path.resolve(__dirname, "dist") }, module: { rules: [ { test: /\.pc$/, loader: "paperclip-loader", options: { // paperclip.config.json can be generated via the @paperclip-ui/cli tool config: require("./paperclip.config.json") } }, // Required since paperclip-loader emits // CSS files { test: /\.css$/, use: ["style-loader", "css-loader"] }, { test: /\.(png|jpe?g|gif)$/i, use: ["file-loader"] } ] }, resolve: { extensions: [".tsx", ".ts", ".js"] } }; ``` That's it! from there you can start using Paperclip in your UIs. For example: ```html
    Hello {children}!
    ``` Then, in React code: ```javascript import * as ui from "./greater.pc"; import React from "react"; import ReactDOM from "react-dom"; ReactDOM.render( Paperclip , document.getElementById("mount")); ``` ☝ This should render: `Hello Paperclip!`. ================================================ FILE: packages/paperclip-website/docs/getting-started-cofigure.md ================================================ ================================================ FILE: packages/paperclip-website/docs/getting-started-cra.md ================================================ --- id: getting-started-cra title: Create React App sidebar_label: Create React App --- This is the setup process for CRA if you're using that in your project. Paperclip works with Webpack 4 and 5. To get started, install these dependencies: ``` npm i @paperclip-ui/compiler-react paperclip-loader --save-dev ``` Next, in the same directory as `package.json`, copy this content to `paperclip.config.json` : ```javascript { "srcDir": "./src" } ``` > `srcDir` is where your `*.pc` files go. More docs on this config can be found [here](configure-paperclip). Next, If you're using CRA, then just run `yarn paperclip build` in your project directory to emit JS files. After that, you can import any component like so: ```javascript import * as myComponentStyles from "./my-component.pc.js"; ``` I'd recommend that you include this in your `.gitignore` too: ```sh *.pc.js *.pc.css ``` Also, to make it easier you can include the build script in your `start` script like so: ```json { "name": "my-app-name", "scripts": { "start": "concurrently \"react-scripts start\" \"paperclip build --watch\"" }, "devDependencies": { "concurrently": "^5.3.0", } } ``` ☝ This will start the Paperclip compiler in parallel with your usual start script. Here's a walkthrough: ![CRA walkthrough](/img/cra-walkthrough.gif) ================================================ FILE: packages/paperclip-website/docs/getting-started-first-ui.md ================================================ --- id: getting-started-first-ui title: Using Paperclip With React sidebar_label: Using With React --- Create a new file in your source directory that's called `GroceryList.pc`, then add this stuff: ```html
      {children}
  • {children}
  • Bagels 🥯 Yakitori 🍢 Tofurky 🦃 Skittles 🌈 ``` > If you're using the VS Code extension, you should be prompted to open a few of this file. If not, you cna run the command: `Paperclip: Open Live Preview`. Paperclip files are just like any ordinary component file, so all we need to do is import PC components into a React component. In the same directory, create a file called `GroceryList.tsx` with this content: ```tsx import * as React from "react"; // this will be "./GroceryList.pc.js" if you're using CRA. import * as styles from "./GroceryList.pc"; export function GroceryList() { const groceries = [ "Milk 🥛", "Water 💧", "Taco seasoning 🌮" ]; return { groceries.map(item => ( {item} )) } ; } ``` You'll notice that _all_ components that are exported from the Paperclip file are now accessible in our React component, and the API is pretty similar to React's. For example, a simple UI like this: ```html
    {children}
    ``` Can be used like this: ```jsx import * as styles from "./Header.pc"; header content ``` Check out the [React API](/docs/usage-react) for more information on all the things you can do with Paperclip files. Back to our demo. If you created a new project with `paperclip init`, change your `entry.tsx` file to this: ```tsx import * as React from "react"; import * as ui from "./hello-paperclip.pc"; import * as ReactDOM from "react-dom"; // import this 👇🏻 import { GroceryList } from "./GroceryList"; const mount = document.createElement("div"); document.body.appendChild(mount); // change to this 👇🏻 ReactDOM.render(, mount); ``` Here's a full walkthrough: ![alt](/img/first-ui-demo.gif) And that's it! You've now fully integrated Paperclip into a React app. ================================================ FILE: packages/paperclip-website/docs/getting-started-new-project.md ================================================ --- id: getting-started-new-project title: New Project sidebar_label: New Project --- If you'd like to create a new project with Paperclip, just run this command in a new directory: ``` npx @paperclip-ui/cli init ``` Then run: ``` npm start ``` And you're good to start developing! ================================================ FILE: packages/paperclip-website/docs/getting-started-vscode.md ================================================ --- id: getting-started-vscode title: Installing the visual tools sidebar_label: Installing visual tools --- Paperclip comes with visual tooling that eliminiates the compile step so that you can see your changes immediately after you save (or type if you're using VS Code extension). You'll be happy to use them! There are two options currently for getting started locally: 1. For VS Code users, you can install the extension. 1. For non-vscode users, you can install the CLI tools. ## VS Code extension I highly recommend using the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=crcn.paperclip-vscode) since you can launch previews directly from the IDE, and changes appear _as you type_. ![alt Realtime editing](/img/vscode-measure.gif) Check out the [VS Code extension guide](guide-vscode) for more info. ## CLI dev server If you don't have VS Code, you can just run the CLI tool: ``` npx paperclip dev ``` This will launch Paperclip's visual tooling in the browser. Changes that are saved locally will appear immediately here. ![alt Realtime editing](/img/demo-dev-server.gif) > Note that there are some limitations to the dev server. For example, you won't be able to move frames around, and you won't be able to jump to source code via `meta + click`. That functionality is only available for the IDE extensions. ================================================ FILE: packages/paperclip-website/docs/getting-started-webpack.md ================================================ --- id: getting-started-webpack title: Configuring Webpack sidebar_label: Webpack --- Paperclip works with Webpack 4 and 5. To get started, install these dependencies: ``` npm i @paperclip-ui/compiler-react paperclip-loader --save-dev ``` Next, in the same directory as `webpack.config.js`, copy this content to `paperclip.config.json`: ```javascript { "srcDir": "./src" } ``` > `srcDir` is where your `*.pc` files go. More docs on this config can be found [here](configure-paperclip). Next, update your Webpack config to look something like this: ```javascript module.exports = { module: { // ... rules: [ // ... { test: /\.pc$/, loader: "paperclip-loader", options: { // config for your Paperclip files config: require("./paperclip.config.json") } }, // CSS loaders required to load styles { test: /\.css$/i, use: ["style-loader", "css-loader"], // this also works too // use: [MiniCssExtractPlugin.loader, "css-loader"] }, // Highly recommend { test: /\.(png|jpe?g|gif|ttf|svg)$/i, use: [ { loader: "file-loader" } ] } ], }, }; ``` Paperclip requires that you use [css-loader](https://webpack.js.org/loaders/css-loader/) in order to work, and either the [style-loader](https://webpack.js.org/loaders/style-loader/), or [mini-css-extract-plugin](https://webpack.js.org/plugins/mini-css-extract-plugin/) to go with that. It's also recommended that you include [url-loader](https://webpack.js.org/loaders/url-loader/) or [file-loader](https://webpack.js.org/loaders/file-loader/) in your webpack config so that you can import images, and other assets into your Paperclip files. After that, you can start using Paperclip in your Webpack project! ================================================ FILE: packages/paperclip-website/docs/guide-compiler.md ================================================ --- id: guide-compilers title: Compilers sidebar_label: Compilers --- > If you prefer to figure things out yourself, a few good place to start would be the [React compiler](https://github.com/paperclipui/paperclip/blob/master/packages/paperclip-compiler-react/src/code-compiler.ts), and [Interim module](https://github.com/paperclipui/paperclip/blob/master/packages/paperclip-interim/src/state/html.ts). This is a very basic guide to getting started with compilers. Compilers should be written in JavaScript (this will be expanded in the future). Here's the general structure of one: ``` @paperclip-ui/compiler-[NAME]/ src/ compile.ts index.ts package.json ``` We'll look at the main entry point into the compiler: `index.ts`. ```tsx import { InterimModule, CompileOptions } from "@paperclip-ui/interim"; import { compile as compile2Code } from "./code-compiler"; /** * Takes the Paperclip AST and returns compiled code */ export const compile = ({module}: CompileOptions) => { const code = compile2Code(module); return { ".my-extension": code, // you can add more translations here. // .rb. }; }; ``` The `module` passed into the `compile` function is the JSON representation of the Paperclip file being compiled. For example, if we look at this: ```html
    Hello world!
    ``` The [InterimModule](https://github.com/paperclipui/paperclip/blob/master/packages/@paperclip-ui/interim/src/state/module.ts) representation of this would be something like: ```javascript { imports: [], components: [ { as: "HelloWorld", kind: "Component", exported: true, isInstance: false, attributes: {}, // class names to attach to this element. These are // associated with the inline styles & other CSS defined // within the document. scopeClassNames: ["_59bb", "_pub-59bb"], children: [ { kind: "Text", value: "Hello world!", } ] } ] } ``` Using the information defined in _just this module_, we can create a simple (although incomplete) compiler: ```javascript import { InterimNodeKind, InterimModule, InterimComponent, InterimText, Interim } from "@paperclip-ui/interim"; export const compile = ( module: InterimModule ) => { return ` ${compileComponents(module)} `; } const compileComponents = (module: InterimModule) => ( module.components.map(compileComponent).join("\n"); ); const compileComponent = (component: InterimComponent) => { const buffer = []; buffer.push( `const ${component.as} = (props) => { return ${compileNode(component)} }` ); // make component accessible to external modules, otherwise // it's for internal-use only if (component.exported) { buffer.push(`export {${component.as}}`); } return buffer.join(""); }; const compileNode = (node: InterimNode) => { switch(node.kind) { case InterimNodeKind.Element: case InterimNodeKind.Component: { return compileElement(node); } case InterimNodeKind.Text: { return compileText(node); } } }; const compileText = (text: Text) => text.value; const compileElement = (element: InterimElement | InterimComponent) => { return ` <${element.tagName}${compileAttributes(element)}> ${compileChildren(element)} `; }; const compileChildren = (element: InterimElement | InterimComponent) => { return element.children.map(compileNode).join(""); }; const compileAttributes = (element: InterimElement | InterimComponent) => { const buffer = [ // You'll also want to include classes defined in // element.attributes.class as well ` class="${element.scopeClassNames.join(" ")}"` ]; // for (const name in element.attributes) { // .. compile attribute here... // } return buffer.join(""); }; ``` Compilers should should render HTML. No need to worry about CSS, that's compiled for you. After you're done, update your `paperclip.config.json` file to point to your compiler: ```javascript { compilerOptions: { target: "./path/to/my-compiler" } } ``` Finally, just run: ``` npx @paperclip-ui/cli build ``` And you should have compiled code! That's it for compiler basics. If you're interested in contributing to the development of Paperclip compilers, feel free to reach out! ================================================ FILE: packages/paperclip-website/docs/guide-dynamic-styles.md ================================================ --- id: guide-dynamic-styles title: Dynamically Changing Styles Using JavaScript sidebar_label: Dynamic Styles --- While Paperclip can cover _most_ of your UI, there will probably be edge cases where you need to compute styles using code. Here's an example Paperclip file: ```html live
    ``` > This code can actually be done purely in CSS, but we'll just use it for this demo anyways. In JavaScript, we can simply add styles like so: ```typescript import {Progress} from "./progress.pc"; ``` That's just about it. Just pass a `style` attribute to Paperclip whenever you need to do styling in JavaScript. And be sure to expose `{style?}` in Paperclip, otherwise it won't work. If you need to stylize nested elements, you can do this: ```html
    ``` ================================================ FILE: packages/paperclip-website/docs/guide-how-to-use.md ================================================ --- id: guide-how-to-use title: Paperclip Basics sidebar_label: The Basics --- You can think of Paperclip as a language that focuses _purely_ on your web application's appearance - just covering HTML, CSS, and basic components. With that, you can construct almost _all_ of your application UI in Paperclip. For example, here's a simple list: ```html live
      {children}
  • {children}
  • Bagels 🥯 Yakitori 🍢 Tofurky 🦃 Skittles 🌈 ``` Here's how you can use the template above in a React app: ```jsx import * as React from "react"; import * as styles from "./GroceryList.pc"; export function GroceryList() { const groceries = [ "Milk 🥛", "Water 💧", "Taco seasoning 🌮" ]; return { groceries.map(item => ( {item} )) } ; } ``` ☝🏻 Basically, all this component is doing is adding dynamic behavior to our Paperclip building blocks, and that's all there is to it between Paperclip UIs and code, really. UIs go in Paperclip, logic goes in code. ================================================ FILE: packages/paperclip-website/docs/guide-migrating-to-paperclip.md ================================================ --- id: guide-migrating-to-paperclip title: Migration Code To And From Paperclip sidebar_label: Migrating code --- Migrating to and from Paperclip is easy since most of Paperclip's patterns are shared across different libraries. ### Migrating from CSS You have a few options here. The easiest option is to just import CSS files directly into your document, for example: ```html
    Some TW component
    ``` This requires _no_ migration effort, and allows you to maintain a boundary between your styles and Paperclip if you want to. If you want to move from CSS however, it's basically just copying and pasting. For example: ```css .container { font-family: sans-serif; color: #F60; font-size: 18px; } .content { padding: 10px; } ``` Just paste this like so: ```html
    {children}
    {children}
    ``` ### Migrating from Styled Components, Emotion, etc For the most part, translating styled components to Paperclip is a 1-1 map. Here's an example of some styled components: ```jsx import styled from "styled-component"; import theme from "path/to/my/theme"; export const Button = styled.button` font-family: ${theme.fontFamily1}; font-size: ${theme.fontSize1}; padding: 8px 16px; border: 2px solid ${theme.borderColor1}; display: inline-block; border-radius: 99px; ${({secondary}) => secondary ? ` background: ${theme.backgroundAlt1}; color: ${theme.textColorAlt1}; ` : ""} `; ``` The translation to Paperclip would be this: ```html // file: button.pc // file: theme.pc ``` After migrating, all you need to do is change your styled component imports: ```jsx // Change this: // import { Button } from "./styles.tsx"; // To this: import { Button } from "./styles.pc"; // Everything else remains the same.
      {todos}
    Pick up keys talk dog Pick up milk } /> Pick up keys talk dog Pick up milk } /> // file: styles.pc ``` To see previews locally, just run the following command in your project directory: ``` yarn paperclip designer ``` You can think of previews as a bit of a scratch pad for your UIs. They're nice to use when building UIs out initially since Paperclip compiles in realtime. You can also think of previews as unit tests for your UIs since previews defined within Paperclip are covered for [visual regressions](visual-regression-tooling). To keep your previews more DRY, you can combine them into one master preview component. For example: ```html }> {showCompanyProfile && } {showMNDA && } {showMNDASigned && } { showSkipDataSync && } { showWelcome && } ``` This way, you keep all of your visual states in one spot which makes it easier to maintain. Here's what the code above looks like: ![onboarding](/img/preview-variants.gif) ================================================ FILE: packages/paperclip-website/docs/guide-theming.md ================================================ --- id: guide-theming title: Theming sidebar_label: Theming --- You can create multiple themes (such as `light`, and `dark`) for your app by putting them in a single `themes.pc` file that contains all of your color variables and other theming properties. For example: ```html ``` Then, be sure to reference these variables in each of your PC files: ```html // file: button.pc
    {children}
    ``` After that, the only thing left to do is apply the theme to the component or any ancestor. If you're applying your theme from app code, that might look something like this: ```jsx import { classNames as themes } from "design-system/theme.pc" import { Button } from "./button.pc"; export function App({darkMode}) { return
    ; } ``` ## Safety around theming We can add some safety around theming with [visual regresion tests](/docs/configure-percy) to ensure that components are being stylized correctly, and that no CSS bugs are introduced. For that, I'd recommend you create theme variants for each of your components. Here's an example: ```html live // file: button.pc
    {children}
    // file: theme.pc ``` As a rule of thumb, I'd recommend adding theme variants for _all_ components, not just some. That way you're more sure to capture every component state with the visual regression tests. After that, all that's left to do is run `percy exec -- percy-paperclip`, and that will look for visual changes in each component that has a preview. That should help prevent CSS bugs. ================================================ FILE: packages/paperclip-website/docs/guide-theming2.md ================================================ --- id: guide-theming2 title: Theming sidebar_label: Theming --- ================================================ FILE: packages/paperclip-website/docs/guide-thinking-in-paperclip.md ================================================ --- id: guide-thinking-in-paperclip title: Thinking In Paperclip sidebar_label: Thinking In Paperclip --- Think of Paperclip as your UI layer that contains _all_ of your HTML & CSS. These files contain no logic, just the appearance. Your React components' role is to add interactivity to these files. Let's start off with a simple list UI in Paperclip: ```html live
      {children}
  • {children}
  • item 1 item 2 item 3 ``` This list contains _all_ of the HTML & CSS for our user interface, and exposes the building blocks - components, that can be used in React code to make the list interactive. Here's the code for our list: ```jsx import React, {useState, useRef} from "react"; import * as styles from "./styles.pc"; export const List = () => { const newInputRef = useRef(); const [listItems, setListItems] = useState([ "item 1", "item 2", "item 3" ]); const onAddItemButtonClick = () => { setListItems([...listItems, newInputRef.current.value]); newInputRef.current.value = ""; } return <> {listItems.map(item => ( {item} ))} }; ``` That's all there is to it between Paperclip UIs and React code. UIs go into Paperclip, logic & behavior goes into code. That's it. We'll start off with the relationship between Paperclip & code. Think of it this way: HTML & CSS goes into Paperclip, logic goes into your React code. Here's an example: ```html <> ``` When you first open up a Paperclip document, just start writing your app's HTML & CSS. Don't worry about components or how the HTML is used in React code, we can handle that later. Let's start with a simple website: ```html live ``` I find it so much easier & faster to write just HTML & CSS first - no components or variables, just plain-old HTML & CSS. Better yet if we use a design file from Sketch or Figma, we can re-use some of the structure. We can iterate over the HTML & CSS until the structure is just right, and then move onto creating the building blocks that are needed in React code. After writing basic HTML & CSS, we can _see_ what needs to be modularized, so let's do that with our website above: Paperclip UIs are basically HTML & CSS with the ability to define components. When you're first starting any document, I'd actually recommend just writing vanilla HTML & CSS off the bat, and later on add components. Here's an example: ```html live
    {content}
    {content}
    ================================================ FILE: packages/paperclip-website/docs/guide-third-party-libraries.md ================================================ --- id: guide-third-party-libraries title: Using Third-party Libraries sidebar_label: Third-party libraries --- ## Third-party CSS If you're using third-party CSS such as Bootstrap or Tailwind, you can just import them into your PC documents like so: ```html
    Something
    ``` The `inject-styles` attribute allows for CSS to be accessible in a document without needing to use a namespace. Keep in mind that styles are only accessible in the document where `inject-styles` is applied, so if you're looking to use CSS throughout your app, you'll need to import that CSS explicitly in every document where you'd like to use it. The [Tailwind example](https://github.com/paperclipui/paperclip/tree/master/examples/tailwind) is a good place to start if you're looking to do this. ## Styling third-party HTML you can style third-party HTML from Paperclip. Here's a simple example: ```html ``` Then, in your application code: ```jsx import * as ui from "./Component.pc"; import SomeThirdPartyComponent from "some-third-paty-component"; ``` ================================================ FILE: packages/paperclip-website/docs/guide-visual-tools.md ================================================ --- id: guide-visual-tools title: Visual Tooling sidebar_label: Visual Tooling --- Hello ================================================ FILE: packages/paperclip-website/docs/guide-vscode.md ================================================ --- id: guide-vscode title: VS Code Extension sidebar_label: VS Code extension --- The [VS Code extension](https://marketplace.visualstudio.com/items?itemName=crcn.paperclip-vscode) provides a richer experience for Paperclp that includes code highlighting, intellisense, and realtime visual development. ![alt Realtime editing](/img/vscode-measure.gif) ### Tips - To **Open a live preview**, execute the `Paperclip: Live Preview` while a `*.pc` file is open. - You can open the **web inspector** by running the command: `Developer: Open Webview Developer tools`. - If the extension stops working for whatever reason (might happen during Alpha), then run the `Developer: Reload Window` command. ================================================ FILE: packages/paperclip-website/docs/guide-why.md ================================================ --- id: guide-why title: Why Paperclip? sidebar_label: Why Paperclip? --- There are a number of reasons why Paperclip was created. ### Web development is slow One of the big reasons why Paperclip was created is to eliminate the refresh rate of web application previews. Here's what I mean: ![Slow HMR demo](/img/slow-hmr.gif) > An example of using hot-module reloading for a large project running in a Docker container. Web development is almost _entirely visual_, and also a very iterative process. All of that time spent tweaking CSS gets expensive, and personally I also find it to be a productivity drain. It really sucks to wait 5 seconds for the browser to see a preview each time a change is made. To a reach a _fidelity_ that designers want is another matter. There's usually a _lot_ of time spent getting styles just right, and it gets even harder to do when projects get bigger. At this point in my career, I've found it pretty normal for web applications to visually be _just about right_, but not entirely. There just isn't enough justification to making UI perfect. Paperclip was created primarily to increase visual development speed. Developers should be free to make user interfaces as fast as they can build them, and be unrestricted by their tooling. And with little effort, they should be able to iterate on their UIs for pixel-perfection. ![alt faster UI](/img/faster-ui.gif) ### Visual regressions Visual regressions are a really tough problem in the web development space, especially when you consider the _cascading_ nature of CSS. One small change to a style rule may introduce a new visual bug into your application. And when styles are global, I've noticed that developers that I've worked with don't feel _confident_ about making CSS changes, and so they leave it alone. What you're left with over time is a large accumulation of magical heaping pile of tech debt that you can't remove. To mitigate that from happening requires social rules around CSS, and that's hard to enforce in my experience, especially as teams grow. Granted, most of the cascading issues around CSS go away when you 1) bring CSS into code, and 2) use child selectors (`.ancestor .descendent`, `.parent > .child`) sparingly. However, visual regressions still happen often enough when you refactor code (e.g: changing the theme). The way to fix this problem is with a tool like [Percy](https://percy.io/) that does visual regression tests. However, that requires manual setup that's time-intensive, and requires lots of energy to make into a process. Confidence is a real concern of mine around developer tooling, and I've noticed that tools that are built around safety are only useful when they make an _entire_ system safe, not just parts of it. When you have blind spots in your codebase, developers may feel like don't know what's safe and what isn't, so it's safer to assume that nothing is. Visual regression test coverage is no exception - you really need a lot of them in order to feel safe from CSS bugs. Paperclip doesn't require you to set up visual regression tests since they're given to you automatically. That's because the language is centered around visual development - developers are encouraged to write previews of their components directly within Paperclip files, which in turn are used for visual regression tests. Take this button for example: ```html live ``` ☝ The previews here are primarily for the developer to see what they're doing. And by doing this, they're also getting visual regression tests, basically for free. All that needs to be done at this point is to run the [Percy CLI tool](configure-percy.md). ![alt percy button snapshot](/img/why/button-percy-demo.gif) Here's what you see in Percy: ![alt percy snapshot](/img/why/percy-button-snapshot.png) There's really no mistaking whether a button state is captured or not because each state needs to be displayed in order for the developer to _create_ it, and the developer is also encouraged to display every visual state of the component since that means they'll get visual regression tests. A nice little feedback loop. Assuming that we have the entire UI built in Paperclip, we can feel a bit more confident about making changes, which goes back to increasing the speed of developing web applications since a developers wouldn't need to smoke-test component style changes - it's done automatically for them. All they need to do is change a style and they're done. ================================================ FILE: packages/paperclip-website/docs/guide-workarounds.md ================================================ --- id: guide-workarounds title: Workarounds sidebar_label: Workarounds --- Paperclip will require some finnessing for certain cases. ## Using third-party CSS If you're using third-party CSS such as Bootstrap or Tailwind, you'll need to move the CSS over to a `*.pc` file. For example: ```css .my-1 { /* tailwind code */ } .my-2 { /* tailwind code */ } ``` Must be converted to Paperclip like so: ```css ``` ================================================ FILE: packages/paperclip-website/docs/guide-writing-components.md ================================================ --- id: guide-writing-components-quickly title: Writing Components Efficiently sidebar_label: Writing Components Efficiently --- I think an easy way to start writing components is to simply write the HTML & CSS first. This involves writing a lot of redundant code up front, but seeing how all of the HTML & CSS is rendered will allow us to indentify what to componentize exactly. Let's use a simple website: ```html live ``` This approach is fast & simple. The components also reveal themselves. Here they are: ☝🏻 This much doesn't require much thought. It's a lot of copy & paste, but that and now we can physically _see_ where the boundaries are for components. Now we can start slicing things up into re-usable components. ================================================ FILE: packages/paperclip-website/docs/guide-writing-previews.md ================================================ --- id: guide-writing-previews title: Organizing Paperclip files sidebar_label: Writing previews --- Still a work in progress! ================================================ FILE: packages/paperclip-website/docs/installation.md ================================================ --- id: installation title: Installation sidebar_label: Installation --- To get started with Paperclip, just run this command in your project directory: ``` npx @paperclip-ui/cli init ``` This will walk you through the setup process that will ask you to pick a target compiler. After it's done running, you'll notice a few new files. Next, just run this command: ``` npx @paperclip-ui/cli build ``` You should notice a few more generated files, **these ones can be imported directly into your codebase**. And that's it! You're ready to start using Paperclip. ================================================ FILE: packages/paperclip-website/docs/introduction.md ================================================ --- id: introduction title: Introduction sidebar_label: Introduction --- Paperclip is a tool that specializes in just the appearance of your application, and it only covers HTML, CSS, and primitive components. ## Motivation Bret Victor's talk on [inventing on principle](https://www.youtube.com/watch?v=PUv66718DII&t=130s) was a major influence for this project. The main question when starting this tool was "how can front-end development feel more like Figma or Sketch?". Front-end development _is visual_, so why not start there? Paperclip is the result of [many iterations trying to answer this question](https://levelup.gitconnected.com/lessons-around-creating-ui-builders-46ceeaea327f). ### Development speed The main issue that Paperclip tries to solve is development speed. Typically you may to wait a few seconds for your HTML & CSS changes to appear in the browser, but with Paperclip, changes appear instantly. Here's a demo of Paperclip in action for a pretty large project: ![Captec.io onboarding](/img/super-fast.gif) ### Explicit CSS One of the problems with CSS is that it's global, and can make it hard to tell what elements are being styled. Paperclip aims to solve that by making CSS scoped. This means that CSS defined or included within each document is only applied to that document. For example: ```html
    I'm red text
    ``` The `div { }` selector is only applied to the elements within this document. To make CSS global, you need to explicitly define `:global(div) { }`-like selectors. This way you know exactly what is and isn't global. ### Visual regressions All previews defined within Paperclip are covered for visual regressions - you can think of this like type safety for UI development. This means that you can easily maintain any HTML & CSS that you write, regardless of how it's all written. ### Other motivations The other goal for Paperclip is to lower the barrier to front-end development for designers that want more control. As the project continues to evolve, there will be more tooling that will enable them to do that (through visual & sync tools). --- ## Who is Paperclip intented for? Paperclip is intended for anyone looking to create Single Page Applications - big or small, and any team size. ### Small projects & startups Paperclip is great for small projects since it will enable you to iterate more quickly on your UIs, and get features out the door more quickly. This is especially useful for startups that depend on user feedback for driving feature development. ### Big projects and teams If you already have a big project, then the benefits of moving over to Paperclip are: - Paperclip wrangles any messy CSS you have by keeping it scoped, and covered for visual regression tests. - Product development moves faster since UI development is faster in Paperclip (developers don't have to wait for their code to compile). - Paperclip gives developers complete freedom to write HTML & CSS however they want, and without the need for BEM, SMACSS, and other styling patterns. ### I'm already using Tailwind / Bulma / Bootstrap / etc. Paperclip compliments existing frameworks like Tailwind and Bootstrap by keeping them _scoped_ to the UI you want them applied to. For example: ```html

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

    sit voluptatem
    ``` 👆 Tailwind in this case is only applied to this document. The benefits to this approach are that: - You know exactly what's being styled - You can avoid vendor lock-in with third-party CSS - You can avoid CSS collisions Paperclip doesn't replace CSS frameworks, it greatly compliments them, and helps keep your codebase in a state that can continue to scale and grow depending on your users' needs. ================================================ FILE: packages/paperclip-website/docs/usage-cli.md ================================================ --- id: usage-cli title: CLI Usage sidebar_label: CLI --- The CLI tool is used primarily to compile Paperclip files into your target framework. ## Installation `yarn add @paperclip-ui/cli --dev` ## Commands ### `paperclip init` Configures Paperclip with your current project & installs compilers. ### `paperclip build` Generates code based on your [paperclip config](configure-paperclip.md). **Options** - `write` - Option to write compiled UI files to disk. Output is otherwise printed in the console log. Currently, files are written to the same directory as the `*.pc` files, so be sure to add `*.pc.*` to your `.gitignore`. - `watch` - Starts the file watcher & rebuilds UIs whenever they change. - `definition` - Generate a typed definition file (Specific to TypeScript) ```sh # Build all Paperclip files and print to stdout yarn paperclip build --print # Build Paperclip files & writes them yarn paperclip build # Starts watcher & writes them whenever they change yarn paperclip build --watch ``` ### `paperclip dev` Start the [visual tooling](visual-tooling.md). ## `paperclip coverage` Generage a coverage report about HTML & CSS that is covered for **visual regressions**. ![alt coverage report](./assets/coverage-report.png) **Options** - `--html` - generate HTML report - `--open` - open the HTML report after generating - `--output` - directory where to generate coverage report. Default is `.paperclip/cov` ```sh # Generate coverage report and open paperclip coverage --html --open # Generate coverage report and output to ./civ paperclip coverage --html --output=cov ``` ## paperclip snapshot Generates a base snapshot of the current branch for visual regression testing. Snapshots are saved to `.paperclip/` **Options** - `--force` - force snapshot if there are unstaged changes. ## paperclip diff [branch] Generates a report of visual changes against a different version of your application. ![alt diff screenshot](./assets/visual-regression-ui.png) **Options** - `--open` - open HTML report when emitted. - `--html` - emit HTML report - `--watch` - run differ in watch mode, re-run when files change. ```sh # diff against master, generate HTML report, and re-run when the current branch changes paperclip diff master --html --watch --open # diff changes against snapshot of this brnach paperclip diff --html --watch --open ``` ================================================ FILE: packages/paperclip-website/docs/usage-react.md ================================================ --- id: usage-react title: Using Paperclip In React Apps sidebar_label: React --- After building your Paperclip files, you can import them just as regular JavaScript modules. For example: ```tsx // I like to keep all of the styles in a single namespace // to communicate that `ui.ComponentName` is a primitive comming from // a Paperclip file. import * as styles from "./counter.pc"; // Another option // import * as styles from "./counter.pc"; import React, { useState } from "react"; export default () => { const [currentCount, setCount] = useState(0); const onClick = () => setCount(currentCount + 1); return {currentCount} ; }; ``` ☝🏻 This example uses the following Paperclip UI file: ```html live
    Current count: {children}
    {children} 50 ``` ## classNames You can import class names that are exported from PC files (using `@export`). **Syntax** ```javascript import * as styles from "./counter.pc";
    ``` here'x how you expose classes for JavaScript usage: **Example** ```html ``` ## Adding props Props can be defined just like any ordinary React component. Take this template for example: ```html
    {children}
    ``` In React code, we can define our `onClick` handler like so: ```jsx import * as styles from "./button.pc"; ``` ## Theming You can easily theme React components by exposing `styles` as a prop on your component. For example: ```tsx import * as defaultStyles from "./GroceryList.pc"; export type GroceryListProps = { styles?: Partial, items: string[] }; export function GroceryList({ items, styles: styleOverrides = {} }: GroceryListProps) { const styles = {...defaultStyles, ...styleOverrides}; return ( {items.map(item => ( {item} ))} ); } ``` Then, to override these styles, just override the base styles like so: ```html {children} ``` All that's left is to set these styles on a JSX component: ```jsx // Main JSX component import { GroceryList } from "./GroceryList"; // Custom styles to define import * as groceryListStyles from "./CustomGroceryList.pc"; ``` ## Demo This is a basic example that uses React, and Webpack. Source code can be found here: https://github.com/paperclipui/paperclip/tree/master/examples/react-basic. ================================================ FILE: packages/paperclip-website/docs/usage-syntax.md ================================================ --- id: usage-syntax title: Paperclip Syntax sidebar_label: Syntax --- ### Basics You can use regular HTML & CSS in Paperclip. For example: ```html live height=260px
    I'm purple!!
    ``` The big difference between Paperclip and regular HTML & CSS is that CSS in Paperclip is scoped. This means that styles won't leak into other documents. You _can_ define global CSS, but you need to be explicit about that using the [`:global()`](#global) selector. You can also override styles into other documents using [selector reference syntax](#class-reference). You can also use styles defined within another document by using the [@export](#@export) or [inject-styles](#inject-styles). ## Styling ### Nested rules Nested rules eliminates some redundancy around defining style selectors. **Syntax**: ```css .parent-rule { /* equivalent to: .parent-rule .child-rule */ .child-rule { } /* equivalent to: .parent-rule--variant */ &--variant-rule { } } ``` **Example**: ```html height=500px // file: nested-style-demo.pc
    Header
    content
    ``` The `&` token can be used to combine the _parent_ selector in nested rules like so: ```html height=550px // file: nested-combine-demo.pc
    Button primary
    Button secondary
    ``` Also note that you can nest `@media` queries like so: ```css div { @media screen and (max-width: 400px) { color: blue; } } ``` ### Element scoping Style blocks that are the defined within elements are _scoped_ to that element. For example: ```html height=320px
    I'm red text! I'm blue text!
    I'm black text
    ``` Declarations defined directly in the style elements like the example above are applied to the parent element. Scoped styles are recommended since they keep your styles & elements together in one spot, which makes them a bit more maintainable. It also provides you an easier way to know exactly what's being styled at a glance. ### :within(ancestor-selector) `:within([ancestor-selector])` allows you to apply styles within a parent or ancestor. ```html height=330px
    Some text
    ``` ### @mixin Style mixins are useful for defining a bundle of style declarations (like `color`, `font-size`) that you then can include into style rules. **Syntax**: ```css @mixin mixin-name { /* style props */ decl-name: decl-value; /* nested rules */ div { color: blue; } /* takes body of include statement */ @content; } ``` **Including mixins syntax**: ```css .my-style { @include mixin-name; @include mixin-with-content { display: block; div { color: blue; } } } ``` **Example**: ```html height=670px
    What has to be broken before you can use it?
    ``` ### Re-using media queries Media queries are re-usable in Paperclip by using the following pattern: ```css @mixin desktop { @media screen and (max-width: 1400px) { @content; } } div { @include desktop { font-size: 24px; } } ``` **Example**: ```html live height=500px // file: main.pc
    {children}
    I'm some content I'm some content // file: breakpoints.pc ``` ### @export The `@export` rule allows you to export styles to other documents, as well as application code. **Syntax**: ```css @export { .my-style { /* styles here */ } @keyframes my-keyframe { /* keyframe code here */ } @mixin my-mixin { /* styles here */ } } ``` **Example**: ```html height=300px // file: main.pc
    Hello again!
    // file: styles.pc ``` Note that you _must_ wrap styles around `@export` if you want to reference them. > On that note, I'd recommend only exporting things that you need in other documents since export keywords (`@export`, `export`) make it clear around what's public & private. You can reference class names in React code like so: ```jsx import * as cx from "classnames"; import * as typography from "./typography.pc";
    ``` Note that `.header-text` is not exported, so it's not available in our app code. ### $class-reference Paperclip allows you to explicitly reference class selectors, which is helpful if you're looking to reference or overrides styles in other documents. **Syntax**: ```html
    ``` **Example**: ```html height=180px // file: main.pc Hello // file: atoms.pc ``` You can also use class references to [override component styles](#overriding-component-styles). ### :global All style rules are scoped by default to the document they're defined in. This ensures that they don't leak & have unintended side-effects. However, there _are_ rare cases when you may need to define a global style rule, such as styling HTML defined outside of Paperclip that doesn't have a way to define a `class` attribute. **Syntax**: ```css :global(.my-selector-here > div ~ .another-selector) { name: value; } ``` Here's an example that stylizes parts of [react-select](https://github.com/JedWatson/react-select): ```html
    {children}
    ``` Here's how you use the above styles in React code: ```jsx import * as ui from './Select.pc'; // Keep the select styles locked in ``` Try to avoid `:global` selectors whenever possible since they leak into other documents, and may result in unintended side-effects. If you need to use `:global`, try to wrap it around a style rule that's scoped to the document. For example: ```css /* Safer to use */ .container { :global(body) { } } ``` ## Import You can import [styles](#styling) & [components](#components) from other files. **Syntax**: ```html ``` **Example**: ```html height=330px // file: main.pc Header content Some content // file: pane.pc
    {children}
    {children}
    {children}
    Nothing here! // file: atoms.pc ``` The import `as` keyword defines a namespace that you can use to access exported properties defined within other documents, like above. **Other examples**: - [Exporting styles](#export) - [Exporting components](#exporting-components) #### inject-styles The `inject-styles` props injects all of the import's exported styles into the current document. For example: ```html
    Styles from tailwind.css
    ``` This is particularly useful for third-party CSS since `inject-styles` includes _all_ CSS selectors (class, ID, element, etc) into the current scope. Note that injected styles are only applied to the current document, so if you're importing components from another file, those components won't be styled. For example: ```html
    Styles from tailwind.css
    ``` Note that `.css` files are a special case since all selectors are automatically exported. If you want to inject styles from a `.pc` file, you'll need to explicitly export the styles that you'd like to inject. For example: ```html ``` 👆 everything that is defined within `export` can be injected into a document. Here's how you use the above example: ```html
    I'm large text
    ``` ## Components Components are your UI building blocks. Just add a `component` attribute to a root element (doesn't have a parent). **Syntax**: ```html ``` **Example**: ```html height=400px
    {children}
    Hooray! ``` ### Exporting components Components can be exported to be used in application code, as well as other documents. **Syntax**: ```html
    ``` **Example**: ```html height=1000px // file: todos.pc
    {children}

    Todos

    {children}
      {children}
  • {children}
  • Wash car Wash car Wash car
    // file: styles.pc ``` Here's how we can use this in our React app: ```jsx import React, { useRef, useState } from "react"; import * as ui from "./todos.pc" const TodoApp = () => { const [todos, setTodos] = useState([ { completed: true, label: "walk dog" }, { completed: true, label: "take out trash" } ]); const onNewInputChange = (event) => { // code to add new todo item here } return {todos.map(({completed, label}, i) => ( {label} ))} ; } ``` We can also use our exported component in other Paperclip documents. Here's an example: ```html height=370px // file: importing-components-demo.pc
    // file: todos.pc
    {children}

    Todos

    {children}
      {children}
  • {children}
  • Clean cat car Wash car Wash car
    Nothing to see here
    // file: styles.pc ``` ### Overriding component styles You can override styles in other components assuming that a component exposes an attribute that's bound to `class`. **Syntax**: ```html attributeBoundToClassName="$class-name" ``` **Example**: ```html height=270px // file: style-override-demo.pc Hello World // file: message.pc
    {children}
    ``` Check out [class references](#class-reference) for more information on how to use `$`. #### Using scoped styles Alternatively, you can overriding your components appearance by using scoped styles like so: ```html height=400px
    {children}
    I'm blue text! ``` Note that you need to provide a `class` in your component for inline style overrides to work. ☝🏻 this approach keeps your overrides together, which can be a bit easier to read. The other benefit to this approach is that your code remains portable since everything's in one spot. ### Changing the tag name You may want to change the native tag name of a component. An example of this is a `Button` component that may be a `button` or `a` tag. **Syntax** ```html ``` **Example** ```html height=270px // file: demo.pc
    ``` ## Bindings Bindings allow you to define dynamic behavior in components. ### Child bindings **Syntax**: ```html
    {children} {anotherSlot}
    ``` **Example**: ```html height=280px

    {children}

    I'm a header
    ``` There will probably be the case where you want to define multiple areas of a component for children to go into. Here's an example of that: ```html height=500px // file: main.pc
    {title}
    {controls}
    {children}
    +
    My header} controls={}> Lorem ipsum dolor sit amet, consectetur adipiscing elit. // file: styles.pc ``` `{title}` and `{controls}` (and technically also `{children}`) are considered _slots_ for child nodes to go into, and they can be filled in via attributes: ```html some title} controls={}> Content children ``` ### Attribute bindings You can define dynamic attributes on your elements. For example: ```html height=450px // file: buttons.pc
    {children}
    // file: styles.pc // file: typography.pc ``` Bindings can also be defined outside of string attributes. For example: ```html
    {title} {children}
    ``` > Ref here is specific to React around referencing DOM nodes. You can also use the shorthand approach like so: ```html
    {title} {children}
    ``` This is particularly useful for making your code more DRY. For example: ```html ``` ### Optional bindings By default, bindings are required. So if you define `{class}` on an element, that property will be required when compiled into application code. To make a binding optional, just add a `?` after the binding name like so: ```html
    {children}
    ``` ☝🏻 Here, `class` is optional, whereas `children` is not. When compiled to TypeScript, here's what you get: ```typescript /* other generated code here */ type MessageProps = { class?: Function, children: ReactNode, }; export const Message: React.FC; ``` ### Variant styles The variant style syntax allows you to conditionally apply styles. For example: **Syntax**: ```html
    ``` **Example**: ```html height=900px
    {children}
    Big header
    Medium header
    Small header
    Regular header
    ``` ## Fragments Fragments are useful if you want to render a collection of elements. For example: ```html height=350px
      {listItems}
  • feed fish
  • feed cat
  • feed me
  • } /> ``` You can also define components from them: ```html
  • Item
  • Item 2
  • Item 3
  • Item 4
    ``` ## Annotations Annotations allow you to specify additional metadata about your elements -- this is used particularly for documentation & other visual helpers when developing your UIs. ### @frame The `@frame` annotation allows to you to specify preview frame dimensions for your element. For example: `x/y/width/height` dimensions can be specified visually in the preview window. To hide frames from rendering, you can specify `visible: false` like so: ```html This frame isn't visible in the preview ``` ================================================ FILE: packages/paperclip-website/docs/usage-troubleshooting.md ================================================ --- id: usage-troubleshooting title: Troubleshooting sidebar_label: Troubleshooting --- Not necessarily gotchas, but things to be aware of when you're using Paperclip. ## Can't override variant styles You may be dealing with CSS specificity issues. Suppose that you have an input: ```html ``` And then you have another component that's using TextInput: ```html ``` ☝🏻 `TextColorOverride` won't actually be applied. This is because to native CSS, the xsmall selector has a _higher priority_. Here's an example to illustrate this: ```html ``` > https://jsfiddle.net/hL20se4m/ ☝🏻 This HTML has the same effect as our Paperclip UI code above. Basically, style rules that have more class & attribute selectors have a higher priority. #### How do you fix this? Just flatten your variants. For example: ```html ``` And _then_ you can apply style overrides: ```html ``` ☝🏻 In this case, `TextColorOverride` properties will be applied. ## CSS is being applied outside of Paperclip The most common cause of this is if you have a CSS class name defined within Paperclip that is also defined globally in your application. For example, here's a PC file: ```html
    ``` If you're coming from global CSS & have `.header` defined, then the template above will also catch that style. > The reason for this is because class names in Paperclip are compiled to _scoped_ + _global_ parts. `
    ` for example is compiled to `
    `. This is by design to allow for `:global` selectors to be applied when you need it. The fix for this is either: - Remove the global CSS. I think this is preferrable since to me, I don't think there shouldn't be any global CSS to begin with (aside from edge cases). - Use a prefix in your class names such as `_` (much like private `_` properties in JavaScript) to add some safety from this happening again. ================================================ FILE: packages/paperclip-website/docs/visual-regression-tooling.md ================================================ --- id: visual-regression-tooling title: Setting Up Visual Regression Tests sidebar_label: Visual regression tools --- Paperclip comes with a number of tools to help you keep track of visual changes. The [code coverage](#html-and-css-coverage) tool helps you figure out how much code is covered for visual changes, and the [snapshot](#snapshot-tests) tool helps you analyze changes across your app. ### HTML and CSS coverage The code coverage tool helps you analyze how much HTML and CSS is visible for snapshot testing. All you need to do is run the following command in your project: ``` yarn paperclip coverage --html --open ``` This will generate an HTML report and open it up in your browser. The report will look something like this: ![alt report summary](./assets/html-report.png) You can navigate this report to find code that's dead or isn't covered for visual testing. Here's what a file looks like: ![alt report summary](./assets/button-report.png) The green section indicates code that's covered for snapshot testing, the red section indicates code that's either dead or needs to be set up for snapshot testing. To set up UIs for snapshot tests, all you need to do is apply the CSS to any visible element in a `*.pc` file. In the case of our button above, we can just create a new [preview](guide-previews) directly in the button file. ![alt button preview](./assets/button-preview.png) The preview code for this is simply: ```html
    Primary Primary:hover Secondary Secondary:hover
    ``` This satisfies our coverage report. Now when we run it, this is what's shown: ![alt better summary](./assets/better-coverage.png) Now this file is ready for snapshot tests. ### Snapshot testing Snapshot testing allows you to keep track of visual changes across your `*.pc` files. This is especially useful in tracking changes that may affect many different files. To use snapshot testing, you'll first need to set up a baseline snapshot, typically on your main or master branch, or whatever branch you currently have in production. Here's how you take a snpshot: ``` git checkout main && && git pull && yarn paperclip snapshot ``` This will store PC file screenshots that we can use as a baseline to check for visual changes. After that, you can run this command on any branch to detect visual changes: ```sh yarn paperclip diff [baseline-branch] --html --open ``` If our baseline branch is `main`, the command would be: ```sh yarn paperclip diff main --html --open ``` Here's an example of some changes made to the button file: ![alt button tweaks](./assets/button-tweaks.png) This is what the diff report looks like for the button file: ![alt button diff](./assets/button-diff.png) You'll notice in the left sidebar that all UIs that are affected by this change are towards the top, and outlined in yellow. Here's another UI that was captured by this change: ![alt other diff](./assets/other-diff.png) ================================================ FILE: packages/paperclip-website/docs/visual-tooling.md ================================================ --- id: visual-tooling title: Reltime Development Preview sidebar_label: Realtime preview --- Paperclip comes with visual tooling that enables you to build your UIs in realtime. The quickest way to start using the visual tools is to run: ``` npx @paperclip-ui/cli designer ``` In your project directory where your Paperclip files live. Here's what you should see: ![demo](assets/paperclip-dev.gif) You can run this window in tandem with your text editor, and changes that you make will instantly appear when you save. ### Tips - To **zoom**, scroll & hold down the `meta` / `ctrl` key. - To **measure between elements**, just select an element, then hold `meta` / `ctrl` by hovering over another element. ================================================ FILE: packages/paperclip-website/docusaurus.config.js ================================================ module.exports = { title: "Paperclip", tagline: "A tiny language that brings scoped CSS to any kind of web application.", url: "https://paperclip.dev", baseUrl: "/", favicon: "img/favicon.ico", organizationName: "crcn", // Usually your GitHub org/user name. projectName: "paperclip", // Usually your repo name. themeConfig: { algolia: { apiKey: "692b36b7f6c2f8b247884702fc82e7c3", indexName: "paperclip", }, prism: { theme: require("prism-react-renderer/themes/oceanicNext"), additionalLanguages: ["php"], }, navbar: { title: "Paperclip", logo: { alt: "Paperclip", src: "img/logo-color.svg", srcDark: "img/logo-dark-2.svg", }, items: [ { to: "/docs/installation", label: "Get Started", position: "left", }, { label: "API", to: "/docs/usage-syntax", position: "left", }, { label: "Blog", to: "/blog", position: "left", }, // { // label: "Playground", // to: "https://playground.paperclip.dev", // position: "left" // }, // { // label: "Why", // to: "/docs/guide-why", // position: "left" // }, // {to: 'blog', label: 'Blog', position: 'left'}, // { // href: "https://chat.paperclip.dev/", // label: "Chat", // position: "right" // }, { href: "https://github.com/paperclipui/paperclip", label: "GitHub", position: "right", }, ], }, footer: { style: "dark", links: [ { title: "Get Started", items: [ { label: "Installation", to: "docs/installation", }, { label: "VS Code", to: "docs/guide-vscode", }, ], }, { title: "API", items: [ { label: "Syntax", to: "docs/usage-syntax", }, { label: "React", to: "docs/usage-react", }, { label: "CLI", to: "docs/usage-cli", }, ], }, { title: "Integrations", items: [ { label: "Percy", to: "docs/configure-percy", }, { label: "Webpack", to: "docs/getting-started-webpack", }, { label: "Jest", to: "docs/configure-jest", }, ], }, { title: "More", items: [ { label: "Help", href: "https://chat.paperclip.dev", }, { href: "https://github.com/paperclipui/paperclip", label: "GitHub", }, { href: "https://twitter.com/paperclipui", label: "Twitter", }, ], }, ], copyright: `Copyright © ${new Date().getFullYear()} Craig Condon`, }, colorMode: { defaultMode: "dark", disableSwitch: true, respectPrefersColorScheme: true, }, }, themes: [ // TODO: turn back on when working again // require.resolve("./plugins/live-editor"), require.resolve("./plugins/paperclip"), ], presets: [ [ "@docusaurus/preset-classic", { docs: { // It is recommended to set document id as docs home page (`docs/` path). sidebarPath: require.resolve("./sidebars.js"), // Please change this to your repo. editUrl: "https://github.com/paperclipui/paperclip/edit/master/packages/paperclip-website/", }, // blog: { // showReadingTime: true, // // Please change this to your repo. // editUrl: // 'https://github.com/facebook/docusaurus/edit/master/website/blog/', // }, theme: { customCss: require.resolve("./src/css/custom.css"), }, googleAnalytics: { trackingID: "UA-173953110-1", }, }, ], ], }; ================================================ FILE: packages/paperclip-website/draft/2021-01-05-scoped-css.md ================================================ --- title: Why CSS scoping and how it works in Paperclip description: How to use Paperclip to slug: scoped-css-with-paperclip authors: - name: Craig Condon title: Creator of Paperclip url: https://github.com/crcn image_url: https://github.com/crcn.png image: https://i.imgur.com/mErPwqL.png hide_table_of_contents: false --- CSS in a small project is manageable, but it increasingly becomes difficult to maintain as codebases get larger. For example, this code *alone* isn’t too problematic: ```html
    ``` I think the simplicity of this is nice, and works fine in a small app. However, it’s a bit fragile when it comes to larger applications, especially when working with other people. If someone accidentally adds a new `.card` class somewhere else in the codebase, my card element above will break. Patterns like BEM or SMACSS help alleviate this problem by providing rules around how to write CSS, but those rules don’t address global CSS which is the larger problem. Paperclip is a domain-specific-language that covers HTML and CSS with a few adjustments, the main one being that CSS is scoped by _default_. Paperclip is a language-agnostic UI layer for web applications that aims to help developers write HTML & CSS that's resilient to bugs by keeping CSS scoped, explicit, provide static analysis tooling to follow CSS and make sure it's being applied correctly, and [visual test](https://paperclip.dev/docs/visual-regression-tooling) tooling to make sure that you're aware of every visual change in your app. It’s aimed to be a bit like TypeScript in the sense of having an extra layer of safety for HTML and CSS. Here’s a simple example: ```html
    Hello World
    ``` Styles in Paperclip are scoped by default. In other words, styles are only applied to the elements in the same document. To illustrate how this works, here’s what a *compiled* Paperclip file might look like in regular HTML & CSS: ```html
    Hello world
    ``` > Example of generated code from a `*.pc` file. > You don’t have to worry about styles leaking into other documents, so you’re free to write HTML & CSS however you want. Of course, you may want to expose styles to other files. Here’s an example of how to do that: ```html ``` The `@export` block is an explicit way to expose styles into other documents. Everything within an export block is considered *public,* and usable in other documents. Everything outside of an export block is considered *private* and only works within the document. Here’s how you use exposed styles in another file: ```html ``` Styles from other documents must be explicitly referenced, which is what you’ll notice above with the `$typography.font-regular` reference. This is helpful since it enables the `typography.pc` file to remain easily refactorable later on. For example, if you remove the `font-regular` style (Or move it outside of the `@export` block), the Paperclip compiler will complain about the `font-regular` reference being used in the `button.pc` file: ![Screen Shot 2021-12-31 at 1.22.58 PM.png](./scoped-css/import-error.png) Along with any other file where `$typography.font-regular` is used. This makes refactoring relatively straightforward since all you need to do is fix whatever Paperclip is complaining about - this should feel similar to TypeScript. When it comes to *global* styles, those need to be made explicit using special syntax. Here’s an example: ```html ``` The global syntax exists mainly to give you an escape hatch for Paperclip when you need to handle edge cases, like integrating with third-party HTML that might not have hooks that Paperclip can tap into. For the most part, Paperclip provides enough functionality so that you’ll rarely need to use `:global`. And when you do need global CSS, you’ll know where it is. Paperclip is generic, and designed for any kind of web application. Taking our example button above, here’s how we might expose it to application code: ```html ``` Paperclip exposes just enough information that allows you to hook UI files into your codebase. This makes it possible for Paperclip to be used in just about any language. For example, you could import this UI file into React like so: ```typescript // src/entry.tsx import React from "react"; import ReactDOM from "react-dom"; import * as styles from "./Button.pc"; ReactDOM.render( Click me! , document.body.getElementById("app") ); ``` If you’re familiar with [Styled Components](https://styled-components.com/), the API is similar. If you want, you can also access class names directly. Using our `typography.pc` example above, here’s how we might access the `font-regular` class in TypeScript: ```jsx //src.entry.tsx import React from "react"; import ReactDOM from "react-dom"; import * as styles from "./typography.pc"; ReactDOM.render(
    Hello world!
    , document.body.getElementById("app") ); ``` Paperclip files compile to strongly typed code, so if `font-regular` is removed from the `typography.pc` file, then TypeScript will complain about that. Suppose you want to use Paperclip with PHP, the usage for that might look something like this: ```php "/signup", "title" => "Log In"), styles\TextInput(array("name" => "email")), styles\TextInput(array("name" => "password")), styles\Button("sign up") ) ); ?> ``` The PHP compiler is currently in development, but this gives you a sense of how Paperclip is aiming to be a general solution. Eventually, the goal is for all web applications to benefit from scoped HTML & CSS. Scoped CSS is just one area that Paperclip is covering to help with scalable HTML & CSS. There's also visual regression coverage, accessibility, responsive development, and other aspects that contribute to solid UI code. Next time I'll talk about how Paperclip helps with all of that. Also, If you'd like to help contribute to Paperclip in any capacity, feel free to reach out! ================================================ FILE: packages/paperclip-website/draft/2021-12-31-avoiding-vendor-lock-in.md ================================================ --- title: Using Paperclip to avoid vendor lock-in description: How to use Paperclip to slug: avoiding-vendor-lock-in-with-paperclip authors: - name: Craig Condon title: Creator of Paperclip url: https://github.com/crcn image_url: https://github.com/crcn.png tags: [howto] image: https://i.imgur.com/mErPwqL.png hide_table_of_contents: false draft: true --- Vendor lock-in with CSS frameworks can be a particularly sticky problem, largely because of the global nature of CSS. Once you pick a CSS library with a team of engineers, you can make a pretty good bet that you’ll be incredibly hard to remove - it can just get too mangled up with the codebase. This can make it a bit nerve-wracking to introduce *any* CSS framework, let alone one into an existing codebase where there's the risk of overriding existing styles. I think that a large chunk of this problem can be avoided by removing *global* aspect of CSS, this is where Paperclip is handy. [Paperclip](http://paperclip.dev) provides a way to keep HTML & CSS sandboxed & explicit. Here’s a basic example: ```html
    I'm blue text!
    ``` This file can be used in other documents like so: ```html

    I'm a blue header!

    I'm small blue text
    ``` And if you want, you can include the entire CSS scope of another document like so: ```html

    I'm a blue header!

    I'm small blue text
    ``` This level of control can also be used with CSS frameworks. For example, here’s a Tailwind + Animate.css example: ```html
    ``` > You can play with this example live here: https://codesandbox.io/s/github/paperclipui/paperclip/tree/master/examples/tailwind-and-animate Tailwind is only applied in this document, and you’re given absolute control *where* the library is used in your application. You can also use other libraries too like Bootstrap, Bulma, etc. What if you want to move away from one? You can easily do that. For example: ```html ``` Suppose you want to switch over to Tailwind, here's an example of how to do that: ```html

    Legacy buttons

    New buttons

    ``` here's what this looks like: ![alt bootstrap v tailwind](./avoid-lock-in/bootstrap-v-tailwind.png) The `class:variant` syntax gives you a way to define variant styles on any element. We're using it above between Bootstrap and Tailwind to make sure that they don't clobber each other. This pattern can also be used throughout to incrementally transition from one framework to another while also continuously deploying these changes behind the scenes (which I find to be a far safer approach to shipping big re-designs like this instead of having a feature branch that may quickly become out of date because people are touching UI code so much). And when you're migration effort is done, all you need do is flip a switch. Paperclip should make it easy to keep them in isolation from the rest of your app. Paperclip should also allow you to more easily experiment with new libraries without worrying about them leaking into other parts of your application. You could even use multiple libraries together without worrying about them colliding with each other ================================================ FILE: packages/paperclip-website/draft/2021-scoped-css-for-everyone.md ================================================ TODO: - Talk about CSS libs being useful for front-end - Paperclip is generic - Works with PHP, Python, Ruby - Single dependency, ================================================ FILE: packages/paperclip-website/package.json ================================================ { "name": "@paperclip-ui/website", "version": "18.5.6", "private": true, "scripts": { "start": "docusaurus clear && docusaurus start", "build": "docusaurus clear && docusaurus build", "serve": "docusaurus serve", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "publish:documentation": "docusaurus deploy" }, "publishConfig": { "access": "public" }, "dependencies": { "@docusaurus/plugin-content-blog": "^2.0.0-beta.14", "@docusaurus/preset-classic": "^2.0.0-beta.14", "@docusaurus/theme-classic": "^2.0.0-beta.14", "@paperclip-ui/repl": "^18.5.6", "caniuse-lite": "^1.0.30001178", "clsx": "^1.1.1", "dedent": "^0.7.0", "empty-module": "^0.0.2", "process": "^0.11.10", "react": "^17.0.0", "react-dom": "^17.0.0" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "devDependencies": { "@docusaurus/core": "^2.0.0-beta.15", "@docusaurus/plugin-content-blog": "^2.0.0-beta.15", "@docusaurus/plugin-google-analytics": "^2.0.0-beta.15", "@docusaurus/preset-classic": "^2.0.0-beta.15", "@docusaurus/theme-classic": "^2.0.0-beta.15", "@paperclip-ui/compiler-react": "^18.5.6", "@tandem-ui/workspace": "^18.5.6", "copy-webpack-plugin": "^6.0.3", "css-loader": "^6.5.1", "file-loader": "^6.2.0", "null-loader": "^4.0.1", "paperclip-loader": "^18.5.6" } } ================================================ FILE: packages/paperclip-website/paperclip.config.json ================================================ { "compilerOptions": { "target": "react" }, "srcDir": "./src" } ================================================ FILE: packages/paperclip-website/plugins/live-editor.js ================================================ const path = require("path"); const webpack = require("webpack"); module.exports = function (context, options) { return { name: "@paperclip-ui/live-editor", getThemePath() { return path.resolve(__dirname, "./theme"); }, configureWebpack(config, isServer) { const plugins = [ new webpack.ProvidePlugin({ process: "process/browser.js", Buffer: ["buffer", "Buffer"], }), ]; if (isServer) { plugins.push( new webpack.ProvidePlugin({ TextDecoder: ["util", "TextDecoder"], TextEncoder: ["util", "TextEncoder"], }) ); } return { experiments: { asyncWebAssembly: true, }, module: { rules: [ { test: /(glob$|fsevents|readdirp|chokidar)/, loader: "null-loader", }, ], }, resolve: { alias: { os: "os-browserify/browser", }, }, plugins, }; }, }; }; ================================================ FILE: packages/paperclip-website/plugins/paperclip.js ================================================ module.exports = function (context, options) { return { name: "@paperclip-ui/plugin", configureWebpack(config, isServer) { config.module.rules = config.module.rules.filter((rule) => { if ( rule.test?.source.includes("svg") || rule.test?.source.includes("ttf") ) { return false; } return true; }); return { module: { rules: [ { test: /\.pc$/, loader: "paperclip-loader", options: { config: require("../paperclip.config.json"), }, }, { test: /\.(ttf|svg)$/i, use: [ { loader: "file-loader", }, ], }, ], }, }; }, }; }; ================================================ FILE: packages/paperclip-website/plugins/shim/index.js ================================================ import { TextDecoder as NodeTextDecoder, TextEncoder as NodeTextEncoder } from "util"; export const TextDecoder = typeof window !== "undefined" ? window.TextDecoder : NodeTextDecoder; export const TextEncoder = typeof window !== "undefined" ? window.TextEncoder : NodeTextEncoder; ================================================ FILE: packages/paperclip-website/plugins/theme/CodeBlock/index.jsx ================================================ import React, { useEffect, useRef, useMemo } from "react"; import CodeBlock from "@theme/CodeBlock"; export default (props) => { if (props.live && false) { return ( {props.children} ); } return ; }; const LiveEditor = ({ children, height = 400, fullScreen, expanded, showAllFrames, noMargin, ...rest }) => { const mountRef = useRef(); const graph = useMemo(() => extractContent(children), [children]); useEffect(() => { if (!mountRef.current || typeof window === "undefined") { return; } const extStyle = {}; if (fullScreen) { Object.assign(extStyle, { height: "100vh", }); } else { Object.assign(extStyle, { height, margin: noMargin !== true ? "16px 0px" : undefined, }); } Object.assign(mountRef.current.style, extStyle); // import("@paperclip-ui/repl/src/app").then((module) => { // const app = new module.App( // { // files: graph, // entry: Object.keys(graph)[0], // activeFrame: showAllFrames ? null : 0, // ...rest, // }, // mountRef.current // ); // app.init(); // }); }, [mountRef.current]); return
    ; }; const extractContent = (text) => { const content = String(text); const files = content.split(/\/\/\s*file:\s*/g).filter(Boolean); const graph = {}; let entry; for (const file of files) { const name = (file.match(/(.*?\.(pc|css))/) || [, "main.pc"])[1]; if (!entry) { entry = name; } const content = file.replace(name, "").trim(); graph["file:///" + name] = content; } return graph; }; ================================================ FILE: packages/paperclip-website/sidebars.js ================================================ module.exports = { docs: [ { type: "doc", id: "installation" }, { type: "doc", id: "usage-syntax" }, { type: "doc", id: "configure-paperclip" }, { type: "doc", id: "usage-cli" }, { type: "category", label: "Code Usage", collapsed: false, items: ["usage-react"] }, { type: "category", label: "Tools", collapsed: false, items: ["visual-tooling", "visual-regression-tooling", "guide-vscode"] }, { type: "category", label: "Integrations", collapsed: false, items: [ "getting-started-webpack", "configure-percy", "configure-jest", "configure-prettier" ] }, { type: "category", label: "Guides", collapsed: false, items: [ // "guide-how-to-use", "guide-migrating-to-paperclip", "guide-third-party-libraries", "guide-previews", // "guide-dynamic-styles", // "guide-modules", // "guide-theming2", "guide-compilers" ] } ] }; ================================================ FILE: packages/paperclip-website/src/css/custom.css ================================================ /* stylelint-disable docusaurus/copyright-header */ /** * Any CSS included here will be global. The classic template * bundles Infima by default. Infima is a CSS framework designed to * work well for content-centric websites. */ /* You can override the default Infima variables here. */ /* https://v2.docusaurus.io/docs/migrating-from-v1-to-v2/#colors */ :root { /* --ifm-color-primary: #5084c9; --ifm-color-primary-dark: #3c75c1; --ifm-color-primary-darker: #386fb7; --ifm-color-primary-darkest: #2e5b96; --ifm-color-primary-light: #6593d0; --ifm-color-primary-lighter: #709bd3; --ifm-color-primary-lightest: #90b1dd; */ --ifm-color-primary: #75b1ff; --ifm-color-primary-dark: #509cff; --ifm-color-primary-darker: #3d91ff; --ifm-color-primary-darkest: #0572ff; --ifm-color-primary-light: #9ac6ff; --ifm-color-primary-lighter: #add1ff; --ifm-color-primary-lightest: #e5f0ff; --ifm-heading-font-weight: 600; --ifm-code-font-size: 95%; --ifm-font-family-base: Open Sans; } .footer--dark { background: rgb(48, 50, 56); } .navbar__title { font-family: Sora; font-weight: 600; letter-spacing: 0.005em; /* font-size: 1.2em; */ } .docusaurus-highlight-code-line { background-color: rgb(72, 77, 91); display: block; margin: 0 calc(-1 * var(--ifm-pre-padding)); padding: 0 var(--ifm-pre-padding); } ================================================ FILE: packages/paperclip-website/src/demos/ice-cream-loader.js ================================================ ================================================ FILE: packages/paperclip-website/src/demos/import-code.js ================================================ const SOURCE = ` // PC files are compiled to plain code & can // be imported as regular modules import * as styles from "./styles.pc"; function GroceryList({groceries}) { return { groceries.map(item => ( {item} )) } ; } `.trim(); export default SOURCE; ================================================ FILE: packages/paperclip-website/src/demos/main.js ================================================ import dedent from "dedent"; export default { "button.pc": dedent(`
    {children}
    `), "button-styles.pc": dedent(` Nothing to see here! `), "typography.pc": dedent(`
    Default text
    Danger text
    `), "colors.pc": dedent(`
    {value}
    `) }; ================================================ FILE: packages/paperclip-website/src/demos/simple-pc.js ================================================ import dedent from "dedent"; /* const SOURCE = ` import * as styles from "./styles.pc"; function GroceryList() { const groceries = [ "Milk 🥛", "Water 💧", "Taco seasoning 🌮" ]; return { groceries.map(item => ( {item}; )) } ; } `.trim(); export default SOURCE; */ const SOURCE = `
      {children}
  • {children}
  • `.trim(); export default SOURCE; ================================================ FILE: packages/paperclip-website/src/demos/site-demo.js ================================================ const SOURCE = `// file: Button.pc Button Button Disabled Negative Primary Primary outline xsmall Small Medium Large `; export default SOURCE; ================================================ FILE: packages/paperclip-website/src/demos/tailwind-and-animate.js ================================================ import { TAILWIND_SOURCE } from "./tailwind.css"; export const TAILWIND_AND_ANIMATE_SOURCE = ` // file: /main.pc

    Register

    // file: /css-modules/tailwind.css ${TAILWIND_SOURCE} `; ================================================ FILE: packages/paperclip-website/src/demos/tailwind.css.js ================================================ export const TAILWIND_SOURCE = `/* ! tailwindcss v3.0.11 | MIT License | https://tailwindcss.com */ /* 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) 2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) */ *, ::before, ::after { box-sizing: border-box; /* 1 */ border-width: 0; /* 2 */ border-style: solid; /* 2 */ border-color: currentColor; /* 2 */ } ::before, ::after { --tw-content: ''; } /* 1. Use a consistent sensible line-height in all browsers. 2. Prevent adjustments of font size after orientation changes in iOS. 3. Use a more readable tab size. 4. Use the user's configured font-family by default. */ html { line-height: 1.5; /* 1 */ -webkit-text-size-adjust: 100%; /* 2 */ -moz-tab-size: 4; /* 3 */ -o-tab-size: 4; tab-size: 4; /* 3 */ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */ } /* 1. Remove the margin in all browsers. 2. Inherit line-height from html so users can set them as a class directly on the html element. */ body { margin: 0; /* 1 */ line-height: inherit; /* 2 */ } /* 1. Add the correct height in Firefox. 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 3. Ensure horizontal rules are visible by default. */ hr { height: 0; /* 1 */ color: inherit; /* 2 */ border-top-width: 1px; /* 3 */ } /* Add the correct text decoration in Chrome, Edge, and Safari. */ abbr:where([title]) { -webkit-text-decoration: underline dotted; text-decoration: underline dotted; } /* Remove the default font size and weight for headings. */ h1, h2, h3, h4, h5, h6 { font-size: inherit; font-weight: inherit; } /* Reset links to optimize for opt-in styling instead of opt-out. */ a { color: inherit; text-decoration: inherit; } /* Add the correct font weight in Edge and Safari. */ b, strong { font-weight: bolder; } /* 1. Use the user's configured mono font family by default. 2. Correct the odd em font sizing in all browsers. */ code, kbd, samp, pre { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */ font-size: 1em; /* 2 */ } /* Add the correct font size in all browsers. */ small { font-size: 80%; } /* Prevent sub and sup elements from affecting the line height in all browsers. */ sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sub { bottom: -0.25em; } sup { top: -0.5em; } /* 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 3. Remove gaps between table borders by default. */ table { text-indent: 0; /* 1 */ border-color: inherit; /* 2 */ border-collapse: collapse; /* 3 */ } /* 1. Change the font styles in all browsers. 2. Remove the margin in Firefox and Safari. 3. Remove default padding in all browsers. */ button, input, optgroup, select, textarea { font-family: inherit; /* 1 */ font-size: 100%; /* 1 */ line-height: inherit; /* 1 */ color: inherit; /* 1 */ margin: 0; /* 2 */ padding: 0; /* 3 */ } /* Remove the inheritance of text transform in Edge and Firefox. */ button, select { text-transform: none; } /* 1. Correct the inability to style clickable types in iOS and Safari. 2. Remove default button styles. */ button, [type='button'], [type='reset'], [type='submit'] { -webkit-appearance: button; /* 1 */ background-color: transparent; /* 2 */ background-image: none; /* 2 */ } /* Use the modern Firefox focus style for all focusable elements. */ :-moz-focusring { outline: auto; } /* Remove the additional :invalid styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) */ :-moz-ui-invalid { box-shadow: none; } /* Add the correct vertical alignment in Chrome and Firefox. */ progress { vertical-align: baseline; } /* Correct the cursor style of increment and decrement buttons in Safari. */ ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { height: auto; } /* 1. Correct the odd appearance in Chrome and Safari. 2. Correct the outline style in Safari. */ [type='search'] { -webkit-appearance: textfield; /* 1 */ outline-offset: -2px; /* 2 */ } /* Remove the inner padding in Chrome and Safari on macOS. */ ::-webkit-search-decoration { -webkit-appearance: none; } /* 1. Correct the inability to style clickable types in iOS and Safari. 2. Change font properties to inherit in Safari. */ ::-webkit-file-upload-button { -webkit-appearance: button; /* 1 */ font: inherit; /* 2 */ } /* Add the correct display in Chrome and Safari. */ summary { display: list-item; } /* Removes the default spacing and border for appropriate elements. */ blockquote, dl, dd, h1, h2, h3, h4, h5, h6, hr, figure, p, pre { margin: 0; } fieldset { margin: 0; padding: 0; } legend { padding: 0; } ol, ul, menu { list-style: none; margin: 0; padding: 0; } /* Prevent resizing textareas horizontally by default. */ textarea { resize: vertical; } /* 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) 2. Set the default placeholder color to the user's configured gray 400 color. */ input::-moz-placeholder, textarea::-moz-placeholder { opacity: 1; /* 1 */ color: #9ca3af; /* 2 */ } input:-ms-input-placeholder, textarea:-ms-input-placeholder { opacity: 1; /* 1 */ color: #9ca3af; /* 2 */ } input::placeholder, textarea::placeholder { opacity: 1; /* 1 */ color: #9ca3af; /* 2 */ } /* Set the default cursor for buttons. */ button, [role="button"] { cursor: pointer; } /* Make sure disabled buttons don't get the pointer cursor. */ :disabled { cursor: default; } /* 1. Make replaced elements display: block by default. (https://github.com/mozdevs/cssremedy/issues/14) 2. Add vertical-align: middle to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) This can trigger a poorly considered lint error in some tools but is included by design. */ img, svg, video, canvas, audio, iframe, embed, object { display: block; /* 1 */ vertical-align: middle; /* 2 */ } /* Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) */ img, video { max-width: 100%; height: auto; } /* Ensure the default browser behavior of the hidden attribute. */ [hidden] { display: none; } *, ::before, ::after { --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-shadow: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-shadow-colored: 0 0 #0000; } .mb-1 { margin-bottom: 0.25rem; } .mt-4 { margin-top: 1rem; } .block { display: block; } .flex { display: flex; } .h-screen { height: 100vh; } .w-full { width: 100%; } .w-screen { width: 100vw; } .max-w-sm { max-width: 24rem; } .items-center { align-items: center; } .justify-center { justify-content: center; } .space-y-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); margin-bottom: calc(1rem * var(--tw-space-y-reverse)); } .rounded-xl { border-radius: 0.75rem; } .rounded-md { border-radius: 0.375rem; } .bg-white { --tw-bg-opacity: 1; background-color: rgb(255 255 255 / var(--tw-bg-opacity)); } .bg-indigo-50 { --tw-bg-opacity: 1; background-color: rgb(238 242 255 / var(--tw-bg-opacity)); } .bg-gradient-to-br { background-image: linear-gradient(to bottom right, var(--tw-gradient-stops)); } .bg-gradient-to-tr { background-image: linear-gradient(to top right, var(--tw-gradient-stops)); } .from-blue-600 { --tw-gradient-from: #2563eb; --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to, rgb(37 99 235 / 0)); } .to-indigo-600 { --tw-gradient-to: #4f46e5; } .px-10 { padding-left: 2.5rem; padding-right: 2.5rem; } .py-8 { padding-top: 2rem; padding-bottom: 2rem; } .px-4 { padding-left: 1rem; padding-right: 1rem; } .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; } .text-center { text-align: center; } .text-2xl { font-size: 1.5rem; line-height: 2rem; } .text-lg { font-size: 1.125rem; line-height: 1.75rem; } .font-semibold { font-weight: 600; } .tracking-wide { letter-spacing: 0.025em; } .text-gray-600 { --tw-text-opacity: 1; color: rgb(75 85 99 / var(--tw-text-opacity)); } .text-indigo-100 { --tw-text-opacity: 1; color: rgb(224 231 255 / var(--tw-text-opacity)); } .shadow-md { --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } .outline-none { outline: 2px solid transparent; outline-offset: 2px; }`; ================================================ FILE: packages/paperclip-website/src/demos/third-party-css.js ================================================ const SOURCE = `

    {description}

    {caption}
    `; export default SOURCE; ================================================ FILE: packages/paperclip-website/src/pages/index-old.js ================================================ import React from "react"; import clsx from "clsx"; import Layout from "@theme/Layout"; import Link from "@docusaurus/Link"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; import useBaseUrl from "@docusaurus/useBaseUrl"; import styles from "./styles.module.css"; const features = [ { title: <>Just the primitives, // imageUrl: 'img/undraw_docusaurus_mountain.svg', description: ( <> Paperclip just covers basic HTML, CSS, and primitive components that you can use in your React code. ) }, { title: <>Visual programming, // imageUrl: 'img/undraw_docusaurus_tree.svg', description: <>Watch your changes live, directly within VS Code. }, { title: <>Visual regression testing, // imageUrl: 'img/undraw_docusaurus_react.svg', description: ( <> Paperclip allows you to test for visual regressions right out of the box. Just run percy-paperclip ) } ]; function Feature({ imageUrl, title, description }) { const imgUrl = useBaseUrl(imageUrl); return (
    {imgUrl && (
    {title}
    )}

    {title}

    {description}

    ); } function BigFeature({ imageUrl, title, description }) { const imgUrl = useBaseUrl(imageUrl); return (

    {title}

    {description}

    ); } function Home() { const context = useDocusaurusContext(); const { siteConfig = {} } = context; return (

    {siteConfig.title}

    {siteConfig.tagline}

    {features && features.length > 0 && (
    {features.map((props, idx) => ( ))}
    )} {/*
    */}
    ); } export default Home; ================================================ FILE: packages/paperclip-website/src/pages/index.js ================================================ import React from "react"; import Layout from "@theme/Layout"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; import IMPORT_CODE_DEMO_SOURCE from "../demos/import-code"; import PRIMITIVE_UI_EXAMPLE from "../demos/simple-pc"; import THIRD_PART_CSS_EXAMPLE from "../demos/third-party-css"; // import CodeBlock from "@theme-init/CodeBlock"; import CodeBlock from "../../plugins/theme/CodeBlock"; import * as styles from "../styles/index.pc"; import * as buttonStyles from "../styles/button.pc"; import * as typography from "../styles/typography.pc"; import { TAILWIND_AND_ANIMATE_SOURCE } from "../demos/tailwind-and-animate"; import { TAILWIND_SOURCE } from "../demos/tailwind.css"; import { Redirect } from "react-router-dom"; function Home() { const context = useDocusaurusContext(); const { siteConfig = {} } = context; // const prismTheme = usePrismTheme(); return (
    Scoped CSS for everyone {/*Paperclip is a UI layer that brings{" "} safe, scalable HTML & CSS to any kind of web application*/} {/* Paperclip is a fast, scalable UI layer that brings scoped CSS to any kind of web application. */} } cta={ <> Get Started {/* Get started Check out on GitHub */} } preview={ {PRIMITIVE_UI_EXAMPLE} } right={ {IMPORT_CODE_DEMO_SOURCE} } > } /> Writing HTML and CSS for large codebases is hard, especially with global styles, slow developer tooling, and lack of automation for catching visual regressions. Paperclip is a tiny language that focuses purely on these problems, with features to help you build maintainable UIs more quickly, and accurately. } /> Paperclip compiles to plain, readable code that also keeps{" "} CSS scoped, so you can write CSS however you want knowing that it'll never accidentally leak into other documents. } /> Paperclip provides a realtime preview to help you build UIs quickly, and{" "} visual regression tooling to help you catch every visual change before shipping to production.{" "} } /> Paperclip is designed to be compiled into whatever language you're using, and currently supports React and static HTML out of the box. You can use Paperclip to compliment your existing codebase to an extra layer of safety. } /> Paperclip gives you explicit syntax for using CSS that gives you complete control over how styles are applied, even by third-party modules. With Paperclip, you never have to worry about style collisions again. , // <>Paperclip keeps CSS frameworks scoped, so you know exactly how they're used in your application. No more worrying about CSS frameworks accidentally overriding styles. // "Feel free to use any CSS framework you want. Paperclip keeps them scoped so that you have complete control how they're used throughout " // "Paperclip keeps CSS frameworks scoped, so you have complete control over how they're used in your application. No more lock-in." // "No more global CSS with. Paperclip gives you complete control over how they're used throughout your application." // "Paperclip enhances your third-pary CSS by keeping it scoped, so you have explicit control over how it's used in your app." ]} ctaText="Learn more" ctaHref="/docs/guide-third-party-libraries" preview={ // {/* {TAILWIND_AND_ANIMATE_SOURCE} */} {THIRD_PART_CSS_EXAMPLE} } /> Paperclip comes with tools to help you easily keep track of{" "} all visual changes across your application, so you can confidently make big CSS changes knowing that you'll be able to track just about every visual bug that comes up. } ctaText="Learn about visual regression tooling" ctaHref="/docs/visual-regression-tooling" preview={} /> No more waiting for code to compile in order to see how your UI looks. Paperclip comes with preview tooling that allows you to build your UIs in realtime, directly within VS Code. Not using VS Code? No worries, you can also launch the visual tools using the CLI command. } ctaText="Learn about the visual tools" ctaHref="/docs/visual-tooling" preview={} // ctaText="View the docs" // ctaHref={"https://paperclip.dev/docs/configure-percy"} /> Paperclip currently compiles to React and{" "} static HTML, and works with frameworks such as{" "} NextJS, and GatsbyJS. } actions={ <> npx @paperclip-ui/cli init yarn add @paperclip-ui/cli --dev && yarn paperclip init } />
    ); } function Home2() { return ; } export default Home2; ================================================ FILE: packages/paperclip-website/src/pages/repl.js ================================================ import React from "react"; // import CodeBlock from "../../plugins/theme/CodeBlock"; const EXAMPLE = ` // file: main.pc
    Hello world
    `; function REPL() { return null; // return ( // // {EXAMPLE} // // ); } export default REPL; ================================================ FILE: packages/paperclip-website/src/pages/styles.module.css ================================================ /* stylelint-disable docusaurus/copyright-header */ /** * CSS files with the .module.css suffix will be treated as CSS modules * and scoped locally. */ .heroBanner { padding: 4rem 0; text-align: center; position: relative; overflow: hidden; } .heroDemo { /* margin: 0px auto; */ margin-top: 24px; /* max-width: 1024px; */ } .preview { border-radius: 4px; border: 2px solid rgba(0, 0, 0, 0.3); box-sizing: border-box; } @media screen and (max-width: 966px) { .heroBanner { padding: 2rem; } } .buttons { display: flex; align-items: center; justify-content: center; } .features { display: flex; align-items: center; padding: 2rem 0; width: 100%; } .nuggets { background: rgb(235, 235, 235); padding: 16px; border-radius: 4px; } .nuggets :global(.col) { border-right: 1px solid rgb(218, 218, 218); } .nuggets :global(.col:last-child) { border-right: none; } .featureImage { height: 200px; width: 200px; } ================================================ FILE: packages/paperclip-website/src/styles/button.pc ================================================ {children} hello world
    Primary Primary:hover Secondary Secondary
    ================================================ FILE: packages/paperclip-website/src/styles/colors.pc ================================================ ================================================ FILE: packages/paperclip-website/src/styles/fonts/open-sans/font-face.pc ================================================ ================================================ FILE: packages/paperclip-website/src/styles/fonts/preview.pc ================================================
    A quick brown fox jumped over the lazy dog
    ================================================ FILE: packages/paperclip-website/src/styles/fonts/roboto/font-face.pc ================================================ ================================================ FILE: packages/paperclip-website/src/styles/fonts/sora/font-face.pc ================================================ ================================================ FILE: packages/paperclip-website/src/styles/icons/icons.pc ================================================
    ================================================ FILE: packages/paperclip-website/src/styles/index-backend.pc ================================================ {children}
    {children}
    {title}
    {description}
    {cta}
    {preview}
    {title}
    {text}
    {children}
    {title}
    {description}
    {example}
    {children}
    {title}
    {description}
    A hybrid approach to building web applications. } description={ Realtime previews, visual regression testing, and more. Paperclip is a template language that comes with tooling to help you build UIs more quickly & safely. } cta={ Sign up for early access } preview={