├── .gitignore ├── .npmrc ├── .travis.yml ├── .yarnrc ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gruntfile.js ├── LICENSE.md ├── NOTICE.md ├── README.md ├── commitlint.config.js ├── docs ├── ARCHITECTURE.md └── DEVELOPING.md ├── greenkeeper.json ├── lerna.json ├── opticss.code-workspace ├── package.json ├── packages ├── @opticss │ ├── attr-analysis-dsl │ │ ├── .npmignore │ │ ├── .vscode │ │ │ ├── cSpell.json │ │ │ ├── launch.json │ │ │ ├── settings.json │ │ │ └── tasks.json │ │ ├── CHANGELOG.md │ │ ├── grammar │ │ │ ├── attrlexer.js │ │ │ ├── attrvalue.js │ │ │ └── attrvalue.ne │ │ ├── package.json │ │ ├── src │ │ │ ├── AttributeValueParser.ts │ │ │ └── index.ts │ │ ├── test │ │ │ ├── mocha.opts │ │ │ └── parser-test.ts │ │ ├── tsconfig.json │ │ ├── tslint.cli.json │ │ ├── tslint.json │ │ └── tslint.release.json │ ├── code-style │ │ ├── .vscode │ │ │ ├── launch.json │ │ │ └── settings.json │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── configs │ │ │ ├── tslint.cli.json │ │ │ ├── tslint.interactive.json │ │ │ └── tslint.release.json │ │ ├── package.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── rules │ │ │ │ ├── index.ts │ │ │ │ ├── multilineParametersRule.ts │ │ │ │ ├── preferUnknownToAnyRule.ts │ │ │ │ └── preferWhateverToAnyRule.ts │ │ ├── test │ │ │ └── rules │ │ │ │ ├── multiline-parameters │ │ │ │ ├── args.ts.fix │ │ │ │ ├── args.ts.lint │ │ │ │ └── tslint.json │ │ │ │ ├── prefer-unknown-to-any │ │ │ │ ├── test.ts.lint │ │ │ │ └── tslint.json │ │ │ │ └── tslint-test.json │ │ ├── tsconfig.json │ │ ├── tslint.cli.json │ │ ├── tslint.json │ │ └── tslint.release.json │ ├── demo-app │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── bin │ │ │ └── opticss │ │ ├── package.json │ │ ├── server.js │ │ ├── src │ │ │ ├── css-size-fake.ts │ │ │ ├── demos.ts │ │ │ ├── index.ts │ │ │ └── simple-example.ts │ │ ├── static │ │ │ ├── app.css │ │ │ └── index.html │ │ ├── test │ │ │ ├── demo-test.ts │ │ │ └── mocha.opts │ │ ├── tsconfig.json │ │ ├── tslint.cli.json │ │ ├── tslint.json │ │ ├── tslint.release.json │ │ └── types-local │ │ │ └── codemirror │ │ │ ├── index.d.ts │ │ │ └── package.json │ ├── element-analysis │ │ ├── .vscode │ │ │ ├── cSpell.json │ │ │ ├── launch.json │ │ │ ├── settings.json │ │ │ └── tasks.json │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src │ │ │ ├── Attribute.ts │ │ │ ├── Element.ts │ │ │ ├── SourceLocation.ts │ │ │ ├── Tagname.ts │ │ │ ├── attrValues.ts │ │ │ └── index.ts │ │ ├── test │ │ │ ├── element-analysis-test.ts │ │ │ └── mocha.opts │ │ ├── tsconfig.json │ │ ├── tslint.cli.json │ │ ├── tslint.json │ │ ├── tslint.release.json │ │ └── yarn-error.log │ ├── simple-template │ │ ├── .npmignore │ │ ├── .vscode │ │ │ ├── cSpell.json │ │ │ ├── launch.json │ │ │ ├── settings.json │ │ │ └── tasks.json │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src │ │ │ ├── SimpleAnalyzer.ts │ │ │ ├── SimpleTemplateRewriter.ts │ │ │ ├── SimpleTemplateRunner.ts │ │ │ ├── TestTemplate.ts │ │ │ └── index.ts │ │ ├── test │ │ │ ├── mocha.opts │ │ │ ├── simple-template-runner-test.ts │ │ │ ├── simple-template-test.ts │ │ │ └── template-analysis-test.ts │ │ ├── tsconfig.json │ │ ├── tslint.cli.json │ │ ├── tslint.json │ │ └── tslint.release.json │ ├── template-api │ │ ├── .vscode │ │ │ ├── cSpell.json │ │ │ ├── launch.json │ │ │ ├── settings.json │ │ │ └── tasks.json │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src │ │ │ ├── BooleanExpression.ts │ │ │ ├── StyleMapping.ts │ │ │ ├── TemplateError.ts │ │ │ ├── TemplateIntegrationOptions.ts │ │ │ └── index.ts │ │ ├── test │ │ │ ├── mocha.opts │ │ │ ├── style-mapping-test.ts │ │ │ └── template-api.ts │ │ ├── tsconfig-test.json │ │ ├── tsconfig.json │ │ ├── tslint.cli.json │ │ ├── tslint.json │ │ └── tslint.release.json │ └── util │ │ ├── .vscode │ │ ├── cSpell.json │ │ ├── launch.json │ │ ├── settings.json │ │ └── tasks.json │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src │ │ ├── IdentityDictionary.ts │ │ ├── Maybe.ts │ │ ├── MultiMap.ts │ │ ├── OptiCSSError.ts │ │ ├── TwoKeyMultiMap.ts │ │ ├── UtilityTypes.ts │ │ ├── assertNever.ts │ │ ├── clean.ts │ │ ├── flatten.ts │ │ ├── index.ts │ │ ├── typedAssert.ts │ │ └── unionInto.ts │ │ ├── test │ │ ├── maybe-test.ts │ │ ├── mocha.opts │ │ ├── util-test.ts │ │ └── utility-types-test.ts │ │ ├── tsconfig.json │ │ ├── tslint.cli.json │ │ ├── tslint.json │ │ └── tslint.release.json ├── opticss │ ├── .vscode │ │ ├── cSpell.json │ │ ├── launch.json │ │ ├── settings.json │ │ └── tasks.json │ ├── ARCHITECTURE.md │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── Actions │ │ │ ├── Action.ts │ │ │ ├── actions │ │ │ │ ├── AnnotateMergeConflict.ts │ │ │ │ ├── ChangeSelector.ts │ │ │ │ ├── ExpandShorthand.ts │ │ │ │ ├── MarkAttributeValueObsolete.ts │ │ │ │ ├── MergeDeclarations.ts │ │ │ │ ├── Note.ts │ │ │ │ ├── RemoveRule.ts │ │ │ │ ├── RewriteRuleIdents.ts │ │ │ │ └── Warning.ts │ │ │ └── index.ts │ │ ├── CssFile.ts │ │ ├── Match │ │ │ ├── AttributeMatcher.ts │ │ │ ├── ElementMatcher.ts │ │ │ ├── Match.ts │ │ │ ├── Matcher.ts │ │ │ ├── TagMatcher.ts │ │ │ └── index.ts │ │ ├── OpticssOptions.ts │ │ ├── OptimizationPass.ts │ │ ├── Optimizer.ts │ │ ├── errors.ts │ │ ├── index.ts │ │ ├── initializers │ │ │ ├── index.ts │ │ │ └── initKnownIdents.ts │ │ ├── optimizations │ │ │ ├── MergeDeclarations │ │ │ │ ├── DeclarationMapper.ts │ │ │ │ ├── OptimizationContext.ts │ │ │ │ ├── StyleInfo.ts │ │ │ │ └── index.ts │ │ │ ├── Optimization.ts │ │ │ ├── RemoveUnusedStyles.ts │ │ │ ├── RewriteIdents.ts │ │ │ └── index.ts │ │ ├── parseSelector.ts │ │ ├── query.ts │ │ └── util │ │ │ ├── IdentGenerator.ts │ │ │ ├── adaptSourceMap.ts │ │ │ ├── cssIntrospection.ts │ │ │ └── shorthandProperties.ts │ ├── test │ │ ├── fixtures │ │ │ └── integration-tests │ │ │ │ └── simple │ │ │ │ ├── markup.html │ │ │ │ └── styles.css │ │ ├── integration-tests.ts │ │ ├── mocha.opts │ │ ├── opticss-test.ts │ │ ├── optimizations │ │ │ ├── all-optimizations-test.ts │ │ │ ├── merge-declarations-test.ts │ │ │ ├── remove-unused-styles-test.ts │ │ │ └── rewrite-idents-test.ts │ │ ├── parse-selector-test.ts │ │ ├── styleable-test.ts │ │ └── util │ │ │ ├── assertCascade.ts │ │ │ ├── assertSmaller.ts │ │ │ ├── clean.ts │ │ │ ├── input.txt │ │ │ └── randomness.ts │ ├── tsconfig.json │ ├── tslint.cli.json │ ├── tslint.json │ ├── tslint.release.json │ └── types-local │ │ ├── concat-with-sourcemaps │ │ ├── index.d.ts │ │ └── package.json │ │ └── css-size │ │ ├── index.d.ts │ │ └── package.json └── resolve-cascade │ ├── .npmignore │ ├── .vscode │ ├── launch.json │ ├── settings.json │ └── tasks.json │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ ├── README.md │ ├── classes │ │ ├── resolve_cascade.cascade.md │ │ ├── resolve_cascade.elementstyle.md │ │ ├── resolve_cascade.elementstylemismatch.md │ │ └── resolve_cascade.markupmismatcherror.md │ └── interfaces │ │ ├── resolve_cascade.assertionresult.md │ │ ├── resolve_cascade.cascadeinformation.md │ │ ├── resolve_cascade.computedstyle.md │ │ ├── resolve_cascade.matchedselector.md │ │ ├── resolve_cascade.pseudostates.md │ │ └── resolve_cascade.styledpseudoelements.md │ ├── package.json │ ├── src │ ├── Cascade.ts │ ├── assertCascade.ts │ ├── index.ts │ └── util.ts │ ├── test │ ├── assert-cascade-test.ts │ ├── cascade-test.ts │ ├── clean.ts │ └── mocha.opts │ ├── tsconfig.json │ ├── tslint.cli.json │ ├── tslint.json │ └── tslint.release.json ├── tsconfig.json ├── tslint.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | coverage/ 4 | build/ 5 | .DS_Store 6 | packages/css-property-parser 7 | yarn-error.log 8 | lerna-debug.log 9 | tsconfig.tsbuildinfo 10 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | if: tag IS blank 2 | git: 3 | depth: 1 4 | sudo: false 5 | language: node_js 6 | node_js: 7 | - "8" 8 | - "10" 9 | - "12" 10 | 11 | before_install: 12 | - npm install -g lerna yarn 13 | 14 | install: 15 | - lerna bootstrap --registry=https://registry.npmjs.org/ 16 | 17 | script: 18 | - commitlint-travis 19 | - lerna run test 20 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | -install.check-files true 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contribution Agreement 2 | ====================== 3 | 4 | As a contributor, you represent that the code you submit is your 5 | original work or that of your employer (in which case you represent you 6 | have the right to bind your employer). By submitting code, you (and, if 7 | applicable, your employer) are licensing the submitted code to LinkedIn 8 | and the open source community subject to the BSD 2-Clause license. 9 | 10 | Getting Started 11 | =============== 12 | 13 | Install required global dependencies: 14 | 15 | ``` 16 | $ npm install -g lerna yarn 17 | ``` 18 | 19 | Check out the code: 20 | 21 | ``` 22 | $ git clone https://github.com/linkedin/opticss.git 23 | ``` 24 | 25 | Go into the `opticss` directory and install dependencies 26 | and make sure all the tests are passing. 27 | 28 | ``` 29 | $ cd opticss 30 | $ lerna bootstrap 31 | $ lerna run test 32 | ``` 33 | 34 | The code for individual packages of this monorepo are in `packages/*`. 35 | Within any of the packages in this monorepo you'll generally use the npm 36 | package scripts to manage the project, E.g. `yarn run test` or 37 | `yarn run lintfix`. Run `yarn run` for a list of available commands. 38 | 39 | 40 | Responsible Disclosure of Security Vulnerabilities 41 | ================================================== 42 | 43 | **Do not file an issue on Github for security issues.** Please review 44 | the [guidelines for disclosure][disclosure_guidelines]. Reports should 45 | be encrypted using PGP ([public key][pubkey]) and sent to 46 | [security@linkedin.com][disclosure_email] preferably with the title 47 | "Vulnerability in Github LinkedIn/opticss - <short summary>". 48 | 49 | 50 | Tips for Getting Your Pull Request Accepted 51 | =========================================== 52 | 53 | 1. Make sure all new features are tested and the tests pass. 54 | 2. Bug fixes must include a test case demonstrating the error that it fixes. 55 | 3. Open an issue first and seek advice for your change before submitting 56 | a pull request. Large features which have never been discussed are 57 | unlikely to be accepted. **You have been warned.** 58 | 59 | [disclosure_guidelines]: https://www.linkedin.com/help/linkedin/answer/62924 60 | [pubkey]: https://gist.github.com/chriseppstein/3f45d3a8e6fb42f24cb7b3f77f21381e 61 | [disclosure_email]: mailto:security@linkedin.com?subject=Vulnerability%20in%20Github%20LinkedIn/opticss%20-%20%3Csummary%3E 62 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | const shell = require("shelljs"); 2 | module.exports = function(grunt) { 3 | grunt.initConfig({ 4 | release: { 5 | options: { 6 | npm: false, 7 | afterRelease: ['publish'] 8 | } 9 | }, 10 | }); 11 | grunt.loadNpmTasks('grunt-release'); 12 | grunt.registerTask('publish', "Publish to NPM", function() { 13 | shell.exec('li-npm-publish'); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 2-CLAUSE LICENSE 2 | 3 | Copyright 2018 LinkedIn Corporation and Contributors. 4 | All Rights Reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /NOTICE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 LinkedIn Corporation and Contributors 2 | All Rights Reserved. 3 | 4 | Licensed under the BSD 2-Clause License (the "License"). 5 | See LICENSE in the project root for license information. 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OptiCSS 2 | ======= 3 | 4 | [![Greenkeeper badge](https://badges.greenkeeper.io/linkedin/opticss.svg)](https://greenkeeper.io/) 5 | 6 | ### OptiCSS is a template-aware stylesheet optimizer. 7 | 8 | Most developers don't use OptiCSS directly. You may be looking for a style framework that uses it: 9 | 10 | * [CSS Blocks](http://css-blocks.com/) 11 | * *Add your project to this list! We want to collaborate with you on adopting OptiCSS.* 12 | 13 | Overall [architecture documentation](./docs/ARCHITECTURE.md). 14 | 15 | This is a monorepo, there's different documentation available in the various packages: 16 | 17 | ### Public API & Libraries: 18 | 19 | These packages are what we expect others to have dependencies on. We are careful about their public APIs and backwards compatibility. 20 | 21 | * `opticss`: The core library containing the optimizer. [README](./packages/opticss/README.md) 22 | * `template-api` - The template analysis API. 23 | * `element-analysis`: The element analysis API. 24 | * `resolve-cascade` - A library that produces a resolved cascade for CSS 25 | selectors against a DOM without using a browser. [README](./packages/resolve-cascade/README.md) 26 | * `util` - Common utilities and data structures that we share across our project. 27 | 28 | ### Internal Dependencies: 29 | 30 | These packages are extracted to allow them to be shared. You probably won't 31 | need to depend on them. As long as the other packages in this monorepo 32 | compile and pass tests we don't worry about backwards incompatibilty for 33 | these. 34 | 35 | * `simple-template` - A custom template language we use for testing OptiCSS. 36 | * `attr-analysis-dsl` - A custom DSL for expressing attribute analysis succinctly. 37 | * `code-style`: The coding styleguide and linters for OptiCSS. [README](./packages/@opticss/code-style/README.md) 38 | 39 | ### Miscellany 40 | * `demo-app`: An interactive demo of OptiCSS. -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'subject-full-stop': [2, 'always', '.'], 5 | 'subject-case': [ 6 | 2, 'always', 7 | ['sentence-case'] 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /docs/DEVELOPING.md: -------------------------------------------------------------------------------- 1 | Developing Software That Uses OptiCSS 2 | ===================================== 3 | 4 | OptiCSS is written in TypeScript and we expect that the code that integrates with it non-trivially will also be written in TypeScript. Except in rare cases, the runtime code does not check for invalid arguments or bad data -- that's what the type checker is for. 5 | -------------------------------------------------------------------------------- /greenkeeper.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": { 3 | "default": { 4 | "packages": [ 5 | "package.json", 6 | "packages/@opticss/attr-analysis-dsl/package.json", 7 | "packages/@opticss/code-style/package.json", 8 | "packages/@opticss/demo-app/package.json", 9 | "packages/@opticss/demo-app/types-local/codemirror/package.json", 10 | "packages/@opticss/element-analysis/package.json", 11 | "packages/@opticss/simple-template/package.json", 12 | "packages/@opticss/template-api/package.json", 13 | "packages/@opticss/util/package.json", 14 | "packages/opticss/package.json", 15 | "packages/opticss/types-local/concat-with-sourcemaps/package.json", 16 | "packages/resolve-cascade/package.json" 17 | ] 18 | } 19 | }, 20 | "ignore": [ 21 | "@types/node" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "independent", 3 | "npmClient": "yarn", 4 | "useWorkspaces": true, 5 | "packages": [ 6 | "packages/*", 7 | "packages/@opticss/*" 8 | ], 9 | "command": { 10 | "publish": { 11 | "message": "chore(release): Publish packages.", 12 | "conventionalCommits": true, 13 | "sort": true 14 | }, 15 | "bootstrap": { 16 | "sort": true 17 | }, 18 | "run": { 19 | "sort": true 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /opticss.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "packages/@opticss/attr-analysis-dsl" 5 | }, 6 | { 7 | "path": "packages/@opticss/demo-app" 8 | }, 9 | { 10 | "path": "packages/@opticss/element-analysis" 11 | }, 12 | { 13 | "path": "packages/opticss" 14 | }, 15 | { 16 | "path": "packages/resolve-cascade" 17 | }, 18 | { 19 | "path": "packages/@opticss/simple-template" 20 | }, 21 | { 22 | "path": "packages/@opticss/template-api" 23 | }, 24 | { 25 | "path": "packages/@opticss/util" 26 | }, 27 | { 28 | "path": "packages/@opticss/code-style" 29 | }, 30 | { 31 | "path": "docs" 32 | }, 33 | { 34 | "path": "." 35 | } 36 | ], 37 | "settings": { 38 | "cSpell.ignorePaths": [ 39 | "**/node_modules/**", 40 | "**/vscode-extension/**", 41 | "**/.git/**", 42 | ".vscode", 43 | "typings", 44 | "**/*.json" 45 | ], 46 | "typescript.tsdk": "opticss/node_modules/typescript/lib", 47 | "cSpell.words": [ 48 | "Opti", 49 | "stylesheet", 50 | "unoptimized" 51 | ], 52 | "files.exclude": { 53 | "**/dist": true 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "opticss", 4 | "devDependencies": { 5 | "@commitlint/cli": "^7.5.2", 6 | "@commitlint/config-conventional": "^7.5.0", 7 | "@commitlint/travis-cli": "^7.5.2", 8 | "husky": "^2.2.0", 9 | "lerna": "^3.13.2" 10 | }, 11 | "scripts": { 12 | "bootstrap": "lerna bootstrap", 13 | "commitmsg": "commitlint --edit $GIT_PARAMS", 14 | "cleandist": "lerna list --toposort -l | awk '{ print $3\"/dist\" }' | xargs rm -rf", 15 | "test": "lerna run test" 16 | }, 17 | "workspaces": [ 18 | "packages/*", 19 | "packages/@opticss/*" 20 | ], 21 | "greenkeeper": { 22 | "commitMessages": { 23 | "addConfigFile": "chore(greenkeeper): Add Greenkeeper config file.", 24 | "updateConfigFile": "chore(greenkeeper): Update Greenkeeper config file.", 25 | "initialBadge": "docs(greenkeeper): Add Greenkeeper badge.", 26 | "initialDependencies": "chore(greenkeeper): Update dependencies.", 27 | "initialBranches": "chore(greenkeeper): Whitelist greenkeeper branches.", 28 | "dependencyUpdate": "chore(greenkeeper): Update ${dependency} to version ${version}.", 29 | "devDependencyUpdate": "chore(greenkeeper): Update ${dependency} to version ${version}.", 30 | "dependencyPin": "chore(greenkeeper): Pin ${dependency} to ${oldVersion}.", 31 | "devDependencyPin": "chore(greenkeeper): Pin ${dependency} to ${oldVersion}.", 32 | "lockfileUpdate": "chore(greenkeeper): Update ${lockfilePath}.", 33 | "closes": "\n\nCloses #${number}." 34 | } 35 | }, 36 | "volta": { 37 | "node": "10.15.3", 38 | "yarn": "1.15.2" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | test/ 3 | ts*.json 4 | -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/.vscode/cSpell.json: -------------------------------------------------------------------------------- 1 | // cSpell Settings 2 | { 3 | // Version of the setting file. Always 0.1 4 | "version": "0.1", 5 | // language - current active spelling language 6 | "language": "en", 7 | // words - list of words to be always considered correct 8 | "words": [ 9 | "Rewriteable", 10 | "opticss" 11 | ], 12 | // flagWords - list of words to be always considered incorrect 13 | // This is useful for offensive words and common spelling errors. 14 | // For example "hte" should be "the" 15 | "flagWords": [] 16 | } -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "preLaunchTask": "compile", 12 | "program": "${workspaceRoot}/node_modules/.bin/_mocha", 13 | "args": [ 14 | "dist/test", 15 | "--opts", 16 | "test/mocha.opts" 17 | ], 18 | "cwd": "${workspaceRoot}", 19 | "outFiles": [ 20 | "${workspaceRoot}/dist/**/*.js" 21 | ], 22 | "sourceMaps": true 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "**/dist/**/*": true, 5 | "**/*.lock": true 6 | }, 7 | "editor.tabSize": 2, 8 | "typescriptHero.codeOutline.enabled": true, 9 | "typescriptHero.resolver.multiLineWrapThreshold": 10, 10 | "typescriptHero.resolver.multiLineTrailingComma": true, 11 | "typescriptHero.resolver.organizeOnSave": true, 12 | "tslint.configFile": "tslint.json", 13 | "tslint.enable": true, 14 | "cSpell.words": [ 15 | "Rewriteable" 16 | ], 17 | "tslint.packageManager": "yarn", 18 | "npm.packageManager": "yarn" 19 | } -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "compile", 8 | "type": "npm", 9 | "script": "compile", 10 | "problemMatcher": [] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.6.3](https://github.com/linkedin/opticss/compare/@opticss/attr-analysis-dsl@0.6.2...@opticss/attr-analysis-dsl@0.6.3) (2019-09-16) 7 | 8 | **Note:** Version bump only for package @opticss/attr-analysis-dsl 9 | 10 | 11 | 12 | 13 | 14 | ## [0.6.2](https://github.com/linkedin/opticss/compare/@opticss/attr-analysis-dsl@0.6.1...@opticss/attr-analysis-dsl@0.6.2) (2019-05-02) 15 | 16 | **Note:** Version bump only for package @opticss/attr-analysis-dsl 17 | 18 | 19 | 20 | 21 | 22 | ## [0.6.1](https://github.com/linkedin/opticss/compare/@opticss/attr-analysis-dsl@0.6.0...@opticss/attr-analysis-dsl@0.6.1) (2019-04-30) 23 | 24 | **Note:** Version bump only for package @opticss/attr-analysis-dsl 25 | 26 | 27 | 28 | 29 | 30 | # [0.6.0](https://github.com/linkedin/opticss/compare/@opticss/attr-analysis-dsl@0.5.0...@opticss/attr-analysis-dsl@0.6.0) (2019-04-25) 31 | 32 | **Note:** Version bump only for package @opticss/attr-analysis-dsl 33 | 34 | 35 | 36 | 37 | 38 | # 0.5.0 (2019-04-19) 39 | 40 | 41 | 42 | # 0.4.0 (2018-10-19) 43 | 44 | 45 | ### Features 46 | 47 | * Manually throw error for Node 6 in Optimizer. ([c4db789](https://github.com/linkedin/opticss/commit/c4db789)) 48 | 49 | 50 | 51 | # 0.3.0 (2018-04-24) 52 | 53 | 54 | 55 | # 0.3.0-rc.0 (2018-04-24) 56 | 57 | 58 | 59 | 60 | 61 | 62 | # [0.3.0](https://github.com/linkedin/opticss/compare/v0.3.0-rc.0...v0.3.0) (2018-04-24) 63 | 64 | **Note:** Version bump only for package @opticss/attr-analysis-dsl 65 | 66 | 67 | 68 | 69 | 70 | 71 | # [0.2.0](https://github.com/linkedin/opticss/compare/v0.1.1...v0.2.0) (2017-11-26) 72 | 73 | 74 | 75 | 76 | **Note:** Version bump only for package @opticss/attr-analysis-dsl 77 | -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/grammar/attrlexer.js: -------------------------------------------------------------------------------- 1 | const moo = require('moo') 2 | 3 | const lexer = moo.compile({ 4 | WS: /[ \t]+/, 5 | pipe: '|', 6 | lparen: '(', 7 | rparen: ')', 8 | asterisk: '*', 9 | unknown: '???', 10 | unknownIdentifier: '?', 11 | absent: '---', 12 | constant: /[^|()*\n \t]+/, 13 | }); 14 | 15 | module.exports = lexer; -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opticss/attr-analysis-dsl", 3 | "version": "0.6.3", 4 | "description": "A DSL for writing attribute analysis objects for use in OptiCSS.", 5 | "main": "dist/src/index.js", 6 | "types": "dist/src/index.d.ts", 7 | "scripts": { 8 | "compile": "tsc --build && mkdir -p dist/src/grammar && cp grammar/*.js dist/src/grammar", 9 | "pretest": "yarn run compile", 10 | "prepublishOnly": "yarn run compile && yarn run lintall", 11 | "lint": "tslint -t msbuild --project . -c tslint.cli.json", 12 | "lintall": "tslint -t msbuild --project . -c tslint.release.json", 13 | "lintfix": "tslint -t msbuild --project . -c tslint.cli.json --fix", 14 | "test": "mocha dist/test --opts test/mocha.opts", 15 | "posttest": "yarn run lint", 16 | "coverage": "istanbul cover -i \"dist/src/**/*.js\" -x \"**/grammar/**\" --dir ./build/coverage _mocha -- dist/test --opts test/mocha.opts && yarn run remap", 17 | "remap": "remap-istanbul -i build/coverage/coverage.json -o coverage -t html", 18 | "docs": "typedoc --out ./docs .", 19 | "grammar": "nearleyc grammar/attrvalue.ne -o grammar/attrvalue.js" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/linkedin/opticss.git" 24 | }, 25 | "keywords": [ 26 | "css", 27 | "cascade" 28 | ], 29 | "author": "Chris Eppstein", 30 | "license": "BSD-2-Clause", 31 | "bugs": { 32 | "url": "https://github.com/linkedin/opticss/issues?q=is%3Aopen+is%3Aissue+label%3Apkg%3Aelement-analysis+label%3Apkg%3Aattr-analysis-dsl" 33 | }, 34 | "homepage": "https://github.com/linkedin/opticss/tree/master/packages/%40opticss/attr-analysis-dsl", 35 | "engines": { 36 | "node": "6.* || 8.* || >= 10.*" 37 | }, 38 | "publishConfig": { 39 | "access": "public" 40 | }, 41 | "dependencies": { 42 | "@opticss/util": "^0.7.0", 43 | "moo": "^0.5.0", 44 | "nearley": "^2.10.3" 45 | }, 46 | "devDependencies": { 47 | "@opticss/code-style": "^0.6.0", 48 | "@opticss/element-analysis": "^0.6.2", 49 | "@types/chai": "^4.0.4", 50 | "@types/mocha": "^5.2.6", 51 | "@types/nearley": "^2.9.0", 52 | "chai": "^4.1.2", 53 | "istanbul": "^0.4.5", 54 | "mocha": "^6.1.4", 55 | "mocha-typescript": "^1.1.9", 56 | "remap-istanbul": "^0.13.0", 57 | "source-map-support": "^0.5.3", 58 | "tslint": "^5.10.0", 59 | "typedoc": "^0.15.0-0", 60 | "typescript": "~3.4.4" 61 | }, 62 | "gitHead": "ef310cb1b10dbc90cae4f859da146863f99d940b", 63 | "toolchain": { 64 | "node": "10.15.3", 65 | "yarn": "1.15.2" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/src/AttributeValueParser.ts: -------------------------------------------------------------------------------- 1 | import { AttributeValue } from "@opticss/element-analysis"; 2 | import * as nearley from "nearley"; 3 | const grammar: nearley.CompiledRules = require("./grammar/attrvalue"); 4 | 5 | export interface AttributeFlags { 6 | [attrName: string]: boolean; 7 | } 8 | 9 | const WHITESPACE_ATTRIBUTES: AttributeFlags = { 10 | "class": true, 11 | }; 12 | 13 | // All attributes that are considered plaintext and cannot contain expressions. 14 | const TEXT_ATTRIBUTES: AttributeFlags = { 15 | "title": true, "media": true, "content": true, "style": true, 16 | }; 17 | 18 | export class AttributeValueParser { 19 | whitespaceAttributes: AttributeFlags; 20 | textAttributes: AttributeFlags; 21 | plainHtml: boolean; 22 | constructor(plainHtml = false, textAttributes: AttributeFlags = {}, whitespaceAttributes: AttributeFlags = {}) { 23 | this.textAttributes = {...TEXT_ATTRIBUTES, ...textAttributes}; 24 | this.whitespaceAttributes = {...WHITESPACE_ATTRIBUTES, ...whitespaceAttributes}; 25 | // support for normal html instead of dynamic attribute expressions. 26 | this.plainHtml = plainHtml; 27 | } 28 | parse(attrNamespace: string | null | undefined, attrName: string, value: string): AttributeValue { 29 | 30 | // Attributes are whitespace delimited if they are `class` attributes with now namespace. 31 | let attrKey = attrName; 32 | if (attrNamespace) attrKey = `${attrNamespace}:${attrKey}`; 33 | let whitespaceDelimited = this.whitespaceAttributes[attrKey]; 34 | 35 | // If whitespace delimited, be sure to trim unused whitespace. 36 | if (whitespaceDelimited) { 37 | value = value.trim(); 38 | } 39 | 40 | // If begins with `javascript:`, or is an `onEvent` attr, it's script -- ignore 41 | if (attrName.match(/^on/) || value.startsWith("javascript:")) { 42 | return { absent: true }; 43 | } 44 | 45 | // If it's a plain text attribute, or is a non-whitespace delimited attr in 46 | // an HTML document, return the constant value. 47 | if (this.textAttributes[attrKey] || (!whitespaceDelimited && this.plainHtml)) { 48 | return { constant: value }; 49 | } 50 | 51 | // Parse the grammar, return AttributeValue object. 52 | let grammarObj = nearley.Grammar.fromCompiled(grammar); 53 | grammarObj.start = whitespaceDelimited ? "whitespaceDelimitedAttribute" : "attribute"; // because this api is stupid. 54 | let parser = new nearley.Parser(grammarObj); 55 | parser.feed(value); 56 | let res = parser.finish(); 57 | return res[0]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./AttributeValueParser"; 2 | -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --require source-map-support/register 3 | --inline-diffs 4 | 5 | -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/test/parser-test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | } from "chai"; 4 | import { 5 | suite, 6 | test, 7 | } from "mocha-typescript"; 8 | 9 | import { AttributeValueParser } from "../src"; 10 | 11 | @suite("Simple Templates") 12 | export class SimpleTemplateTest { 13 | @test "Can parse"() { 14 | let parser = new AttributeValueParser(); 15 | let value = parser.parse(null, "class", "(foo | bar)"); 16 | assert.deepEqual(value, {oneOf: [{constant: "foo"}, {constant: "bar"}]}); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "baseUrl": "dist", 6 | "types": [ 7 | "@types/chai", 8 | "@types/node", 9 | "@types/mocha" 10 | ] 11 | }, 12 | "include": [ 13 | "src", 14 | "test" 15 | ], 16 | "exclude": [ 17 | "dist", 18 | "node_modules" 19 | ], 20 | "references": [ 21 | {"path": "../code-style"}, 22 | {"path": "../element-analysis"} 23 | ] 24 | } -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/tslint.cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.cli.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@opticss/code-style/configs/tslint.interactive.json" 3 | } -------------------------------------------------------------------------------- /packages/@opticss/attr-analysis-dsl/tslint.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.release.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/code-style/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch via NPM", 11 | "cwd": "${workspaceFolder}", 12 | "runtimeExecutable": "npm", 13 | "runtimeArgs": [ 14 | "run-script", 15 | "debug" 16 | ], 17 | "port": 9229 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /packages/@opticss/code-style/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "transpiled" 4 | ] 5 | } -------------------------------------------------------------------------------- /packages/@opticss/code-style/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.6.0](https://github.com/linkedin/opticss/compare/@opticss/code-style@0.5.0...@opticss/code-style@0.6.0) (2019-04-25) 7 | 8 | 9 | ### Features 10 | 11 | * Replace prefer-whatever-to-any with prefer-unknown-to-any. ([c1bbadc](https://github.com/linkedin/opticss/commit/c1bbadc)) 12 | 13 | 14 | ### BREAKING CHANGES 15 | 16 | * The lint rule prefer-whatever-to-any is no longer valid 17 | because it relies on a type that was removed. The new 18 | prefer-unknown-to-any lint rule should be used instead. An error is 19 | raised if there's a configuration that enables the old rule. 20 | 21 | 22 | 23 | 24 | 25 | # 0.5.0 (2019-04-19) 26 | 27 | 28 | 29 | # 0.4.0 (2018-10-19) 30 | 31 | 32 | ### Features 33 | 34 | * Manually throw error for Node 6 in Optimizer. ([c4db789](https://github.com/linkedin/opticss/commit/c4db789)) 35 | 36 | 37 | 38 | # 0.3.0 (2018-04-24) 39 | 40 | 41 | 42 | # 0.3.0-rc.0 (2018-04-24) 43 | 44 | 45 | 46 | 47 | 48 | 49 | # [0.3.0](https://github.com/linkedin/opticss/compare/v0.3.0-rc.0...v0.3.0) (2018-04-24) 50 | 51 | **Note:** Version bump only for package @opticss/code-style 52 | -------------------------------------------------------------------------------- /packages/@opticss/code-style/configs/tslint.interactive.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "rules": { 4 | "no-var-keyword": true, 5 | "adjacent-overload-signatures": true, 6 | "no-for-in-array":true, 7 | "prefer-unknown-to-any": true, 8 | "no-reference": true, 9 | "no-eval": true, 10 | "no-floating-promises": true, 11 | "no-invalid-template-strings": true, 12 | "no-string-throw": true, 13 | "switch-default": true, 14 | "no-unnecessary-type-assertion": true, 15 | "label-position": true, 16 | "no-construct": true, 17 | "no-duplicate-variable": true, 18 | "no-inferrable-types": true, 19 | "no-unused-expression": true, 20 | "semicolon": [true, "always"], 21 | "triple-equals": true, 22 | "class-name": true, 23 | "no-default-export": true, 24 | "new-parens": true, 25 | "variable-name": [ 26 | true, 27 | "check-format", 28 | "ban-keywords", 29 | "allow-leading-underscore" 30 | ], 31 | "no-redundant-jsdoc": true, 32 | "binary-expression-operand-order": true, 33 | "prefer-for-of": true, 34 | "max-line-length": [ 35 | false, 36 | {"limit": 120} 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/@opticss/code-style/configs/tslint.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "./tslint.cli.json", 4 | "rules": { 5 | "no-debugger": true, 6 | "no-console": [true, "log"] 7 | } 8 | } -------------------------------------------------------------------------------- /packages/@opticss/code-style/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opticss/code-style", 3 | "author": "Chris Eppstein", 4 | "description": "Configuration and tools to manage the code style of css-blocks.", 5 | "license": "BSD-2-Clause", 6 | "version": "0.6.0", 7 | "main": "configs/tslint.interactive.json", 8 | "readme": "README.md", 9 | "keywords": [ 10 | "tslint", 11 | "tslint-plugin" 12 | ], 13 | "types": "dist", 14 | "scripts": { 15 | "compile": "which tsc && tsc --version && tsc --build", 16 | "pretest": "yarn run compile", 17 | "which": "echo $PATH", 18 | "test": "tslint --test test/rules/*/*", 19 | "debug": "echo `pwd` && node --inspect-brk=9229 tslint --test test/rules/*/*", 20 | "posttest": "yarn run lint", 21 | "lint": "tslint -t msbuild -p . -c tslint.cli.json", 22 | "lintall": "tslint -t msbuild -p . -c tslint.release.json", 23 | "lintfix": "tslint -t msbuild -p . --fix -c tslint.cli.json", 24 | "prepublishOnly": "yarn run compile && yarn run lintall" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/linkedin/opticss/issues?q=is%3Aopen+is%3Aissue+label%3Apkg%3Acode-style" 28 | }, 29 | "repository": { 30 | "url": "https://github.com/linkedin/opticss.git", 31 | "type": "git" 32 | }, 33 | "homepage": "https://github.com/linkedin/opticss/tree/master/packages/%40opticss/code-style", 34 | "engines": { 35 | "node": "6.* || 8.* || >= 10.*" 36 | }, 37 | "publishConfig": { 38 | "access": "public" 39 | }, 40 | "peerDependencies": { 41 | "tslint": "^5.10.0", 42 | "typescript": "~3.4.4" 43 | }, 44 | "files": [ 45 | "dist", 46 | "configs", 47 | "*.md", 48 | "test" 49 | ], 50 | "gitHead": "ef310cb1b10dbc90cae4f859da146863f99d940b", 51 | "toolchain": { 52 | "node": "10.15.3", 53 | "yarn": "1.15.2" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/@opticss/code-style/src/index.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable-next-line:no-default-export 2 | export default "hi"; 3 | -------------------------------------------------------------------------------- /packages/@opticss/code-style/src/rules/index.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable-next-line:no-default-export 2 | export default { 3 | rulesDirectory: ".", 4 | }; 5 | -------------------------------------------------------------------------------- /packages/@opticss/code-style/src/rules/preferUnknownToAnyRule.ts: -------------------------------------------------------------------------------- 1 | import * as Lint from "tslint"; 2 | import * as ts from "typescript"; 3 | 4 | export class Rule extends Lint.Rules.AbstractRule { 5 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 6 | return this.applyWithWalker(new PreferUnknownToAny(sourceFile, this.getOptions())); 7 | } 8 | } 9 | 10 | // The walker takes care of all the work. 11 | class PreferUnknownToAny extends Lint.RuleWalker { 12 | visitAnyKeyword(node: ts.Node): void { 13 | let fix = this.createReplacement(node.getStart(), 3, "unknown"); 14 | this.addFailureAtNode(node, "Using `any` is usually a bad idea. Consider using `unknown` instead.", fix); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/@opticss/code-style/src/rules/preferWhateverToAnyRule.ts: -------------------------------------------------------------------------------- 1 | import * as Lint from "tslint"; 2 | import * as ts from "typescript"; 3 | 4 | export class Rule extends Lint.Rules.AbstractRule { 5 | public apply(_sourceFile: ts.SourceFile): Lint.RuleFailure[] { 6 | throw new Error("prefer-whatever-to-any has been replaced by prefer-unknown-to-any. Please update your configuration."); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/@opticss/code-style/test/rules/multiline-parameters/args.ts.fix: -------------------------------------------------------------------------------- 1 | const foo = ( 2 | asdf, 3 | xyz 4 | ) => { 5 | return true; 6 | }; 7 | 8 | const foo = ( 9 | asdf, 10 | ...baz 11 | ) => { return true; } 12 | 13 | const foo = ( 14 | asdf, 15 | baz 16 | ) => { return true; } 17 | 18 | const foo = ( 19 | asdf, 20 | ...baz 21 | ) => { return true; } 22 | 23 | const foo = function( 24 | asdf, 25 | xyz 26 | ) { 27 | return true; 28 | }; 29 | 30 | function foo( 31 | asdf, 32 | xyz 33 | ) { 34 | } 35 | 36 | class Foo { 37 | constructor( 38 | one, 39 | two 40 | ) { 41 | 42 | } 43 | foo(asdf: boolean, xyz: string) { 44 | } 45 | fooBar( 46 | asdf: boolean, 47 | xyz: "asdf" 48 | ); 49 | fooBar( 50 | asdf: boolean, 51 | xyz: string 52 | ) { 53 | } 54 | } 55 | 56 | const f = new Foo( 57 | 1, 58 | 2); 59 | 60 | (new Foo()).fooBar( 61 | true, 62 | "asdf"); 63 | 64 | foo( 65 | "asdf", 66 | "xyz"); 67 | 68 | 69 | @decorate( 70 | "first", 71 | "second) 72 | const foo = 1; -------------------------------------------------------------------------------- /packages/@opticss/code-style/test/rules/multiline-parameters/args.ts.lint: -------------------------------------------------------------------------------- 1 | const foo = (asdf, 2 | ~~~~ [Newline expected before first multi-line parameter.] 3 | xyz) => { 4 | ~~~ [Newline expected after last multi-line parameter.] 5 | return true; 6 | }; 7 | 8 | const foo = ( 9 | asdf, 10 | ...baz) => { return true; } 11 | ~~~~~~ [Newline expected after last multi-line parameter.] 12 | 13 | const foo = ( 14 | asdf, 15 | baz) => { return true; } 16 | ~~~[Newline expected after last multi-line parameter.] 17 | 18 | const foo = ( 19 | asdf, 20 | ...baz 21 | ) => { return true; } 22 | 23 | const foo = function(asdf, 24 | ~~~~ [Newline expected before first multi-line parameter.] 25 | xyz) { 26 | ~~~ [Newline expected after last multi-line parameter.] 27 | return true; 28 | }; 29 | 30 | function foo(asdf, 31 | ~~~~ [Newline expected before first multi-line parameter.] 32 | xyz) { 33 | ~~~ [Newline expected after last multi-line parameter.] 34 | } 35 | 36 | class Foo { 37 | constructor(one, 38 | ~~~ [Newline expected before first multi-line parameter.] 39 | two) { 40 | ~~~ [Newline expected after last multi-line parameter.] 41 | 42 | } 43 | foo(asdf: boolean, xyz: string) { 44 | } 45 | fooBar(asdf: boolean, 46 | ~~~~~~~~~~~~~ [Newline expected before first multi-line parameter.] 47 | xyz: "asdf"); 48 | ~~~~~~~~~~~ [Newline expected after last multi-line parameter.] 49 | fooBar(asdf: boolean, 50 | ~~~~~~~~~~~~~ [Newline expected before first multi-line parameter.] 51 | xyz: string) { 52 | ~~~~~~~~~~~ [Newline expected after last multi-line parameter.] 53 | } 54 | } 55 | 56 | const f = new Foo(1, 57 | ~ [Newline expected before first multi-line argument.] 58 | 2); 59 | 60 | (new Foo()).fooBar(true, 61 | ~~~~ [Newline expected before first multi-line argument.] 62 | "asdf"); 63 | 64 | foo("asdf", 65 | ~~~~~~ [Newline expected before first multi-line argument.] 66 | "xyz"); 67 | 68 | 69 | @decorate("first", 70 | ~~~~~~~ [Newline expected before first multi-line argument.] 71 | "second) 72 | const foo = 1; -------------------------------------------------------------------------------- /packages/@opticss/code-style/test/rules/multiline-parameters/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint-test.json" 3 | } -------------------------------------------------------------------------------- /packages/@opticss/code-style/test/rules/prefer-unknown-to-any/test.ts.lint: -------------------------------------------------------------------------------- 1 | let foo: () => any; 2 | ~~~ [Using `any` is usually a bad idea. Consider using `unknown` instead.] 3 | 4 | function foo(bar: any): any { 5 | ~~~ [Using `any` is usually a bad idea. Consider using `unknown` instead.] 6 | ~~~ [Using `any` is usually a bad idea. Consider using `unknown` instead.] 7 | return true; 8 | } -------------------------------------------------------------------------------- /packages/@opticss/code-style/test/rules/prefer-unknown-to-any/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint-test.json" 3 | } -------------------------------------------------------------------------------- /packages/@opticss/code-style/test/rules/tslint-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../dist/rules", 3 | "rules": { 4 | "multiline-parameters": true, 5 | "prefer-unknown-to-any": true 6 | } 7 | } -------------------------------------------------------------------------------- /packages/@opticss/code-style/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "include": [ 4 | "**/*.ts" 5 | ], 6 | "compilerOptions": { 7 | "rootDir": "src", 8 | "outDir": "dist", 9 | "baseUrl": "dist", 10 | "types": [ 11 | "@types/chai", 12 | "@types/node", 13 | "@types/mocha" 14 | ] 15 | } 16 | } -------------------------------------------------------------------------------- /packages/@opticss/code-style/tslint.cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "./configs/tslint.cli.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/code-style/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./configs/tslint.release.json" 3 | } -------------------------------------------------------------------------------- /packages/@opticss/code-style/tslint.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "./configs/tslint.release.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"], 3 | "plugins": ["transform-es2015-modules-commonjs"], 4 | "only": "./dest/**/*.js", 5 | "sourceType": "module" 6 | } 7 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | demo/ 3 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/.npmignore: -------------------------------------------------------------------------------- 1 | demo/ 2 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.5.7](https://github.com/linkedin/opticss/compare/@opticss/demo-app@0.5.6...@opticss/demo-app@0.5.7) (2020-09-13) 7 | 8 | **Note:** Version bump only for package @opticss/demo-app 9 | 10 | 11 | 12 | 13 | 14 | ## [0.5.6](https://github.com/linkedin/opticss/compare/@opticss/demo-app@0.5.5...@opticss/demo-app@0.5.6) (2020-08-06) 15 | 16 | **Note:** Version bump only for package @opticss/demo-app 17 | 18 | 19 | 20 | 21 | 22 | ## [0.5.5](https://github.com/linkedin/opticss/compare/@opticss/demo-app@0.5.4...@opticss/demo-app@0.5.5) (2020-08-04) 23 | 24 | **Note:** Version bump only for package @opticss/demo-app 25 | 26 | 27 | 28 | 29 | 30 | ## [0.5.4](https://github.com/linkedin/opticss/compare/@opticss/demo-app@0.5.3...@opticss/demo-app@0.5.4) (2019-12-09) 31 | 32 | **Note:** Version bump only for package @opticss/demo-app 33 | 34 | 35 | 36 | 37 | 38 | ## [0.5.3](https://github.com/linkedin/opticss/compare/@opticss/demo-app@0.5.2...@opticss/demo-app@0.5.3) (2019-09-16) 39 | 40 | **Note:** Version bump only for package @opticss/demo-app 41 | 42 | 43 | 44 | 45 | 46 | ## [0.5.2](https://github.com/linkedin/opticss/compare/@opticss/demo-app@0.5.1...@opticss/demo-app@0.5.2) (2019-05-02) 47 | 48 | **Note:** Version bump only for package @opticss/demo-app 49 | 50 | 51 | 52 | 53 | 54 | ## [0.5.1](https://github.com/linkedin/opticss/compare/@opticss/demo-app@0.5.0...@opticss/demo-app@0.5.1) (2019-04-30) 55 | 56 | 57 | ### Bug Fixes 58 | 59 | * Upgrade parse5 to 5.0.0 to enable browser builds. ([6a88a33](https://github.com/linkedin/opticss/commit/6a88a33)) 60 | 61 | 62 | 63 | 64 | 65 | # [0.5.0](https://github.com/linkedin/opticss/compare/@opticss/demo-app@0.4.1...@opticss/demo-app@0.5.0) (2019-04-25) 66 | 67 | **Note:** Version bump only for package @opticss/demo-app 68 | 69 | 70 | 71 | 72 | 73 | ## 0.4.1 (2019-04-19) 74 | 75 | 76 | ### Bug Fixes 77 | 78 | * **types:** Ignore errors for null document locations. ([2468cc7](https://github.com/linkedin/opticss/commit/2468cc7)) 79 | 80 | 81 | 82 | # 0.4.0 (2018-10-19) 83 | 84 | 85 | 86 | # 0.3.0 (2018-04-24) 87 | 88 | 89 | 90 | # 0.3.0-rc.0 (2018-04-24) 91 | 92 | 93 | 94 | 95 | 96 | 97 | # [0.3.0](https://github.com/linkedin/opticss/compare/v0.3.0-rc.0...v0.3.0) (2018-04-24) 98 | 99 | **Note:** Version bump only for package @opticss/demo-app 100 | 101 | 102 | 103 | 104 | 105 | 106 | # [0.2.0](https://github.com/linkedin/opticss/compare/v0.1.1...v0.2.0) (2017-11-26) 107 | 108 | 109 | 110 | 111 | **Note:** Version bump only for package @opticss/demo-app 112 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/bin/opticss: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const { DemoOptimizer } = require("@opticss/demo-cli"); 4 | 5 | let optimizer = new DemoOptimizer("demo/input.html", "demo/input.css", "demo"); 6 | optimizer.run(); -------------------------------------------------------------------------------- /packages/@opticss/demo-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opticss/demo-app", 3 | "private": "true", 4 | "version": "0.5.7", 5 | "description": "Demonstrates OptiCSS on simple html and css files.", 6 | "main": "dist/src/DemoOptimizer.js", 7 | "types": "dist/src/index.d.ts", 8 | "scripts": { 9 | "demo": "bin/opticss", 10 | "compile": "tsc --build && babel dist --out-dir dist && browserify --full-paths ./dist/src/index.js > ./dist/out.js", 11 | "pretest": "yarn run compile", 12 | "test": "mocha dist/test --opts test/mocha.opts", 13 | "posttest": "yarn run lint", 14 | "prepublishOnly": "yarn run compile && yarn run lintall", 15 | "lint": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json", 16 | "lintall": "tslint -t msbuild --project tsconfig.json -c tslint.release.json", 17 | "lintfix": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json --fix", 18 | "coverage": "istanbul cover -i \"dist/src/**/*.js\" --dir ./build/coverage _mocha -- dist/test --opts test/mocha.opts", 19 | "remap": "remap-istanbul -i build/coverage/coverage.json -o coverage -t html", 20 | "docs": "typedoc --out ./docs .", 21 | "start": "node server.js", 22 | "watch": "watch 'yarn run compile' './src' './static' './test' --wait=1" 23 | }, 24 | "bin": "bin/opticss", 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/linkedin/opticss.git" 28 | }, 29 | "browser": { 30 | "css-size": "./dist/src/css-size-fake.js" 31 | }, 32 | "author": "Chris Eppstein", 33 | "license": "BSD-2-Clause", 34 | "bugs": { 35 | "url": "https://github.com/linkedin/opticss/issues" 36 | }, 37 | "homepage": "https://github.com/linkedin/opticss/tree/master/packages/%40opticss/demo-app", 38 | "dependencies": { 39 | "@opticss/simple-template": "^0.6.4", 40 | "@types/codemirror": "^0.0.74", 41 | "@types/prettier": "^1.7.0", 42 | "@types/split.js": "^1.3.0", 43 | "babel-cli": "^6.26.0", 44 | "codemirror": "^5.15.2", 45 | "express": "^4.15.5", 46 | "opticss": "^0.9.0", 47 | "parse5": "^5.0.0", 48 | "prettier": "1.17.1", 49 | "split.js": "^1.3.5" 50 | }, 51 | "devDependencies": { 52 | "@opticss/code-style": "^0.6.0", 53 | "@opticss/util": "^0.7.0", 54 | "@types/mkdirp": "^0.5.1", 55 | "@types/parse5": "^5.0.0", 56 | "@types/rimraf": "^2.0.2", 57 | "babel-cli": "^6.26.0", 58 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 59 | "babel-preset-env": "^1.6.0", 60 | "browserify": "amiller-gh/browserify", 61 | "gzip-js": "^0.3.2", 62 | "mkdirp": "^0.5.1", 63 | "moo": "^0.5.0", 64 | "nearley": "^2.10.3", 65 | "rimraf": "^2.6.2", 66 | "source-map-support": "^0.5.3", 67 | "tslint": "^5.10.0", 68 | "typescript": "~3.4.4", 69 | "watch": "^1.0.2" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | app.use('/src', express.static('src')); 5 | app.use('/dist', express.static('dist')); 6 | app.use('/node_modules', express.static('./node_modules')); 7 | app.use('/node_modules', express.static('../../../node_modules')); 8 | app.use(express.static('static')); 9 | 10 | app.listen(3000, function () { 11 | console.log('Opticss demo server listening on port 3000!') 12 | }); 13 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/src/css-size-fake.ts: -------------------------------------------------------------------------------- 1 | const gzip = require("gzip-js"); 2 | 3 | // TODO: Add in-app brotli compression. 4 | 5 | function getBinarySize(str: string) { 6 | return Buffer.byteLength(str, "utf8"); 7 | } 8 | 9 | export interface SizeResults { 10 | in: number; 11 | out: number; 12 | inZip: number; 13 | outZip: number; 14 | // inBrotli: number; 15 | // outBrotli: number; 16 | } 17 | 18 | export function process(pre: string, opts: {level?: number}, post: () => Promise<{css: string}>): Promise { 19 | return post().then((post) => { 20 | 21 | let zipPre = gzip.zip(pre, { 22 | level: opts.level || 6, 23 | name: "pre.css", 24 | timestamp: Date.now() / 1000, 25 | }); 26 | 27 | let zipPost = gzip.zip(post.css, { 28 | level: opts.level || 6, 29 | name: "post.css", 30 | timestamp: Date.now() / 1000, 31 | }); 32 | 33 | return Promise.resolve({ 34 | in: getBinarySize(pre), 35 | out: getBinarySize(post.css), 36 | inZip: zipPre.length, 37 | outZip: zipPost.length, 38 | }); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/src/simple-example.ts: -------------------------------------------------------------------------------- 1 | // This file is used in the OptiCSS README. If it requires changing, update the README too. 2 | import { unknownElement } from "@opticss/element-analysis"; 3 | import { Template, TemplateAnalysis, TemplateIntegrationOptions } from "@opticss/template-api"; 4 | import * as fs from "fs"; 5 | import { OptiCSSOptions, Optimizer } from "opticss"; 6 | 7 | // App level control of enabled features, enabling a feature that 8 | // the template integration doesn't support, has no effect. 9 | const options: Partial = { 10 | rewriteIdents: { id: false, class: true }, 11 | }; 12 | 13 | // This value usually comes from the template integration library. 14 | const templateOptions: Partial = { 15 | rewriteIdents: { id: false, class: true }, 16 | analyzedTagnames: true, 17 | }; 18 | 19 | // A new optimizer instance is required for every optimization. 20 | let opticss = new Optimizer(options, templateOptions); 21 | 22 | // Usually the css file(s) being optimized come from a previous build step. 23 | // So opticss supports input source maps to ensure that errors and debugging 24 | // information is correctly reported. 25 | let cssFile = "build/assets/myapp.css"; 26 | opticss.addSource({ 27 | filename: cssFile, 28 | content: fs.readFileSync(cssFile, "utf-8"), 29 | sourceMap: fs.readFileSync(`${cssFile}.map`, "utf-8"), 30 | }); 31 | 32 | // This example doesn't use an analyzer. Instead, we create an analysis that 33 | // describes an "unknown template" about which nothing is known. 34 | let analysis = new TemplateAnalysis(new Template("unknown.html")); 35 | // Any element that is unknown tells the optimizer that it must assume 36 | // that any two styles might be co-located on that element. This 37 | // forces the optimizer to be extremely conservative with cascade changes. 38 | analysis.elements.push(unknownElement()); 39 | opticss.addAnalysis(analysis); 40 | 41 | // Perform the optimization and output the results. 42 | let outputName = "build/assets/optimized.css"; 43 | // tslint:disable-next-line:no-floating-promises 44 | opticss.optimize(outputName).then(result => { 45 | fs.writeFileSync(outputName, result.output.content.toString(), "utf-8"); 46 | fs.writeFileSync(`${outputName}.log`, result.actions.logStrings().join("\n"), "utf-8"); 47 | // Source map information with opticss is of dubious value because sourcemaps cannot 48 | // represent mutations that turn multiple source bytes into a single output byte. 49 | // So the source map will only surface (at most) one of the source bytes that caused 50 | // an output byte to exist. The log has more in-depth information about what mutations were 51 | // performed. 52 | if (result.output.sourceMap) { 53 | fs.writeFileSync(`${outputName}.map`, result.output.sourceMap, "utf-8"); 54 | } 55 | // When integrating with template analysis we would pass the optimization 56 | // styleMapping result to the template rewriter. 57 | //let rewriteInfo = result.styleMapping; 58 | }); 59 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 55 | 56 |
57 |
58 |
59 |
60 | 61 |
62 |
63 |
64 | 65 |
66 | 67 |
68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/test/demo-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import { skip, suite } from "mocha-typescript"; 3 | 4 | @suite("Demo App") 5 | export class DemoAppTest { 6 | @skip "TODO: Write tests"() { 7 | assert.ok(1); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --require source-map-support/register 3 | --inline-diffs 4 | 5 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "target": "es2015", 5 | "module": "commonjs", 6 | "lib": [ "es2016", "dom" ], 7 | "moduleResolution": "node", 8 | "noImplicitAny": true, 9 | "removeComments": false, 10 | "outDir": "dist", 11 | "baseUrl": "dist", 12 | "types": [ 13 | "@types/chai", 14 | "@types/node", 15 | "@types/mocha" 16 | ], 17 | "paths": { 18 | "codemirror/*": [ "../types-local/codemirror" ], 19 | "parse5": ["../types-local/parse5/index.d.ts"], 20 | "parse5-sax-parser": ["../types-local/parse5-sax-parser/index.d.ts"] 21 | } 22 | }, 23 | "include": [ 24 | "src", 25 | "test" 26 | ], 27 | "exclude": [ 28 | "dist", 29 | "node_modules" 30 | ], 31 | "references": [ 32 | {"path": "../simple-template"}, 33 | {"path": "../../opticss"} 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/tslint.cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.cli.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@opticss/code-style/configs/tslint.interactive.json" 3 | } -------------------------------------------------------------------------------- /packages/@opticss/demo-app/tslint.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.release.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/types-local/codemirror/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "codemirror/mode/css/css" { 2 | const content: any; 3 | export default content; 4 | } 5 | 6 | declare module "codemirror/mode/htmlmixed/htmlmixed" { 7 | const content: any; 8 | export default content; 9 | } 10 | 11 | declare module "codemirror/addon/hint/css-hint" { 12 | const content: any; 13 | export default content; 14 | } 15 | 16 | declare module "codemirror/addon/hint/show-hint" { 17 | const content: any; 18 | export default content; 19 | } 20 | -------------------------------------------------------------------------------- /packages/@opticss/demo-app/types-local/codemirror/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": {}, 3 | "description": "TypeScript definitions for sigma", 4 | "name": "@types/codemirror", 5 | "typings": "index.d.ts", 6 | "version": "0.4.0" 7 | } 8 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/.vscode/cSpell.json: -------------------------------------------------------------------------------- 1 | // cSpell Settings 2 | { 3 | // Version of the setting file. Always 0.1 4 | "version": "0.1", 5 | // language - current active spelling language 6 | "language": "en", 7 | // words - list of words to be always considered correct 8 | "words": [ 9 | "Combinator", 10 | "Combinators", 11 | "Eppstein", 12 | "Opti", 13 | "Opticss", 14 | "Rewritable", 15 | "Rewriteable", 16 | "Selectable", 17 | "Selectorish", 18 | "asdf", 19 | "atrule", 20 | "brotli", 21 | "classname", 22 | "contenteditable", 23 | "deserialize", 24 | "deserializer", 25 | "eppsteins", 26 | "idents", 27 | "klass", 28 | "mergeable", 29 | "mergeables", 30 | "nearley", 31 | "optimizable", 32 | "optimizer's", 33 | "promisify", 34 | "pseudoelement", 35 | "pseudoelements", 36 | "selectables", 37 | "serializable", 38 | "shorthands", 39 | "sourcemap", 40 | "superset", 41 | "supersets" 42 | ], 43 | // flagWords - list of words to be always considered incorrect 44 | // This is useful for offensive words and common spelling errors. 45 | // For example "hte" should be "the" 46 | "flagWords": [ 47 | "hte" 48 | ] 49 | } -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "preLaunchTask": "compile", 12 | "program": "${workspaceRoot}/node_modules/.bin/_mocha", 13 | "args": [ 14 | "dist/test", 15 | "--opts", 16 | "test/mocha.opts" 17 | ], 18 | "cwd": "${workspaceRoot}", 19 | "outFiles": [ 20 | "${workspaceRoot}/dist/**/*.js" 21 | ], 22 | "sourceMaps": true 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "**/dist/**/*": true, 5 | "**/*.lock": true 6 | }, 7 | "editor.tabSize": 2, 8 | "typescriptHero.codeOutline.enabled": true, 9 | "typescriptHero.resolver.multiLineWrapThreshold": 10, 10 | "typescriptHero.resolver.multiLineTrailingComma": true, 11 | "typescriptHero.resolver.organizeOnSave": true, 12 | "typescript.tsdk": "node_modules/typescript/lib", 13 | "tslint.enable": true, 14 | "tslint.configFile": "tslint.json", 15 | "typescript.useCodeSnippetsOnMethodSuggest": true, 16 | "typescriptHero.resolver.ignorePatterns": [ 17 | "dist", 18 | "node_modules", 19 | "test", 20 | "index.ts" 21 | ], 22 | "tslint.packageManager": "yarn", 23 | "npm.packageManager": "yarn" 24 | } -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "compile", 8 | "type": "npm", 9 | "script": "compile", 10 | "problemMatcher": [] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.6.2](https://github.com/linkedin/opticss/compare/@opticss/element-analysis@0.6.1...@opticss/element-analysis@0.6.2) (2019-09-16) 7 | 8 | **Note:** Version bump only for package @opticss/element-analysis 9 | 10 | 11 | 12 | 13 | 14 | ## [0.6.1](https://github.com/linkedin/opticss/compare/@opticss/element-analysis@0.6.0...@opticss/element-analysis@0.6.1) (2019-04-30) 15 | 16 | **Note:** Version bump only for package @opticss/element-analysis 17 | 18 | 19 | 20 | 21 | 22 | # [0.6.0](https://github.com/linkedin/opticss/compare/@opticss/element-analysis@0.5.0...@opticss/element-analysis@0.6.0) (2019-04-25) 23 | 24 | **Note:** Version bump only for package @opticss/element-analysis 25 | 26 | 27 | 28 | 29 | 30 | # 0.5.0 (2019-04-19) 31 | 32 | 33 | 34 | # 0.4.0 (2018-10-19) 35 | 36 | 37 | ### Features 38 | 39 | * Manually throw error for Node 6 in Optimizer. ([c4db789](https://github.com/linkedin/opticss/commit/c4db789)) 40 | 41 | 42 | 43 | # 0.3.0 (2018-04-24) 44 | 45 | 46 | 47 | # 0.3.0-rc.0 (2018-04-24) 48 | 49 | 50 | 51 | 52 | 53 | 54 | # [0.3.0](https://github.com/linkedin/opticss/compare/v0.3.0-rc.0...v0.3.0) (2018-04-24) 55 | 56 | **Note:** Version bump only for package @opticss/element-analysis 57 | 58 | 59 | 60 | 61 | 62 | 63 | # [0.2.0](https://github.com/linkedin/opticss/compare/v0.1.1...v0.2.0) (2017-11-26) 64 | 65 | 66 | 67 | 68 | **Note:** Version bump only for package @opticss/element-analysis 69 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opticss/element-analysis", 3 | "version": "0.6.2", 4 | "description": "Data structures for storing the analysis of elements.", 5 | "main": "dist/src/index.js", 6 | "types": "dist/src/index.d.ts", 7 | "scripts": { 8 | "compile": "tsc --build", 9 | "pretest": "yarn run compile", 10 | "test": "mocha dist/test --opts test/mocha.opts", 11 | "posttest": "yarn run lint", 12 | "prepublishOnly": "yarn run compile && yarn run lintall", 13 | "lint": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json", 14 | "lintall": "tslint -t msbuild --project tsconfig.json -c tslint.release.json", 15 | "lintfix": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json --fix", 16 | "coverage": "istanbul cover -i \"dist/src/**/*.js\" --dir ./build/coverage _mocha -- dist/test --opts test/mocha.opts", 17 | "remap": "remap-istanbul -i build/coverage/coverage.json -o coverage -t html", 18 | "docs": "typedoc --out ./docs ." 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/linkedin/opticss.git" 23 | }, 24 | "keywords": [ 25 | "css", 26 | "cascade" 27 | ], 28 | "author": "Chris Eppstein", 29 | "license": "BSD-2-Clause", 30 | "bugs": { 31 | "url": "https://github.com/linkedin/opticss/issues?q=is%3Aopen+is%3Aissue+label%3Apkg%3Aelement-analysis" 32 | }, 33 | "homepage": "https://github.com/linkedin/opticss/tree/master/packages/%40opticss/element-analysis", 34 | "engines": { 35 | "node": "6.* || 8.* || >= 10.*" 36 | }, 37 | "publishConfig": { 38 | "access": "public" 39 | }, 40 | "dependencies": { 41 | "@opticss/util": "^0.7.0" 42 | }, 43 | "devDependencies": { 44 | "@opticss/code-style": "^0.6.0", 45 | "@types/chai": "^4.0.4", 46 | "@types/mocha": "^5.2.6", 47 | "chai": "^4.1.2", 48 | "istanbul": "^0.4.5", 49 | "mocha": "^6.1.4", 50 | "mocha-typescript": "^1.1.9", 51 | "remap-istanbul": "^0.13.0", 52 | "source-map-support": "^0.5.3", 53 | "tslint": "^5.10.0", 54 | "typedoc": "^0.15.0-0", 55 | "typescript": "~3.4.4" 56 | }, 57 | "gitHead": "ef310cb1b10dbc90cae4f859da146863f99d940b", 58 | "toolchain": { 59 | "node": "10.15.3", 60 | "yarn": "1.15.2" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/src/Element.ts: -------------------------------------------------------------------------------- 1 | import { Attr, Attribute, SerializedAttribute } from "./Attribute"; 2 | import { attrValues } from "./attrValues"; 3 | import { SourceLocation } from "./SourceLocation"; 4 | import { POSITION_UNKNOWN, SourcePosition } from "./SourceLocation"; 5 | import { SerializedTagname, Tag, Tagname } from "./Tagname"; 6 | 7 | export type Selectable = Element | Tag | Attr; 8 | 9 | export interface ElementInfo { 10 | sourceLocation?: SourceLocation; 11 | tagname: TagnameType; 12 | attributes: Array; 13 | id?: string; 14 | } 15 | 16 | export type SerializedElementInfo = ElementInfo; 17 | 18 | export class Element implements ElementInfo { 19 | sourceLocation: SourceLocation; 20 | tagname: Tag; 21 | attributes: Array; 22 | id: string | undefined; 23 | constructor(tagname: Tag, attributes: Array, sourceLocation?: SourceLocation, id?: string) { 24 | this.tagname = tagname; 25 | this.attributes = attributes; 26 | this.sourceLocation = sourceLocation || {start: POSITION_UNKNOWN}; 27 | this.id = id; 28 | } 29 | 30 | static fromElementInfo(info: ElementInfo): Element { 31 | return new Element(info.tagname, info.attributes, info.sourceLocation, info.id); 32 | } 33 | 34 | serialize(): SerializedElementInfo { 35 | let e: SerializedElementInfo = { 36 | tagname: this.tagname.toJSON(), 37 | attributes: this.attributes.map(a => a.toJSON()), 38 | }; 39 | if (this.sourceLocation && this.sourceLocation.start.line >= 0) { 40 | e.sourceLocation = this.sourceLocation; 41 | } 42 | return e; 43 | } 44 | toString() { 45 | let parts = []; 46 | parts.push(this.tagname); 47 | for (let attr of this.attributes) { 48 | parts.push(attr); 49 | } 50 | return `<${parts.join(" ")}>`; 51 | } 52 | } 53 | 54 | /** 55 | * Returns an unknown element. Use of this element in an analysis 56 | * has the effect of putting the optimizer into a conservative optimization 57 | * mode since there's no longer any guarantees about what values maybe be 58 | * correlated on the element's attributes. Essentially, every selector will 59 | * match this element. 60 | * 61 | * @param [unknownAttrs=["class", "id"]] Which attributes should be marked as unknown. 62 | * @param [startPosition] if the start position is known it can be provided. 63 | * @param [endPosition] if the end position is known it can be provided. 64 | */ 65 | export function unknownElement(unknownAttrs = ["class", "id"], startPosition?: SourcePosition, endPosition?: SourcePosition): Element { 66 | let tagname = new Tagname(attrValues.unknown()); 67 | let attrs = new Array(); 68 | for (let attrName of unknownAttrs) { 69 | attrs.push(new Attribute(attrName, attrValues.unknown())); 70 | } 71 | let loc: SourceLocation | undefined = undefined; 72 | if (startPosition) { 73 | loc = {start: startPosition}; 74 | if (endPosition) { 75 | loc.end = endPosition; 76 | } 77 | } 78 | return new Element(tagname, attrs, loc); 79 | } 80 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/src/SourceLocation.ts: -------------------------------------------------------------------------------- 1 | export const POSITION_UNKNOWN: SourcePosition = { line: -1 }; 2 | 3 | export interface SourcePosition { 4 | filename?: string; 5 | line: number; 6 | column?: number; 7 | } 8 | 9 | export function isSourcePosition(position: object): position is SourcePosition { 10 | return !!((position).line); 11 | } 12 | 13 | export interface SourceLocation { 14 | start: SourcePosition; 15 | end?: SourcePosition; 16 | } 17 | 18 | export function isSourceLocation(location: object): location is SourceLocation { 19 | return !!((location).start); 20 | } 21 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/src/attrValues.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AttributeValueChoice, 3 | AttributeValueChoiceOption, 4 | AttributeValueSet, 5 | AttributeValueSetItem, 6 | ValueAbsent, 7 | ValueConstant, 8 | ValueEndsWith, 9 | ValueStartsAndEndsWith, 10 | ValueStartsWith, 11 | ValueUnknown, 12 | ValueUnknownIdentifier, 13 | } from "./Attribute"; 14 | 15 | export namespace attrValues { 16 | export function constant(constant: string): ValueConstant { 17 | return {constant}; 18 | } 19 | export function unknown(): ValueUnknown { 20 | return {unknown: true}; 21 | } 22 | export function unknownIdentifier(): ValueUnknownIdentifier { 23 | return {unknownIdentifier: true}; 24 | } 25 | export function absent(): ValueAbsent { 26 | return {absent: true}; 27 | } 28 | export function startsWith(startsWith: string, whitespace?: boolean): ValueStartsWith { 29 | return {startsWith, whitespace}; 30 | } 31 | export function endsWith(endsWith: string, whitespace?: boolean): ValueEndsWith { 32 | return {endsWith, whitespace}; 33 | } 34 | export function startsAndEndsWith(startsWith: string, endsWith: string, whitespace?: boolean): ValueStartsAndEndsWith { 35 | return {startsWith, endsWith, whitespace}; 36 | } 37 | export function allOf(allOf: Array): AttributeValueSet { 38 | return {allOf}; 39 | } 40 | export function oneOf(oneOf: Array): AttributeValueChoice { 41 | return {oneOf}; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Attribute"; 2 | export * from "./attrValues"; 3 | export * from "./Element"; 4 | export * from "./SourceLocation"; 5 | export * from "./Tagname"; 6 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/test/element-analysis-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import { suite, test } from "mocha-typescript"; 3 | 4 | import { 5 | Attribute, 6 | AttributeValueSet, 7 | Element, 8 | Tagname, 9 | attrValues, 10 | } from "../src"; 11 | 12 | @suite("Element Analysis") 13 | export class ElementAnalysisTest { 14 | @test "attribute value generators"() { 15 | assert.deepEqual(attrValues.absent(), {absent: true}); 16 | assert.deepEqual(attrValues.allOf([attrValues.constant("foo")]), { 17 | allOf: [{constant: "foo"}], 18 | }); 19 | } 20 | @test "css-blocks class analysis"() { 21 | let value: AttributeValueSet = JSON.parse('{"allOf":[{"constant":"header__emphasis"},{"constant":"typography__underline"},{"oneOf":[{"allOf":[{"constant":"with-dynamic-classes__world"},{"oneOf":[{"absent":true},{"constant":"with-dynamic-classes__world--thick"}]}]},{"absent":true}]},{"oneOf":[{"absent":true},{"constant":"header__emphasis--style-bold"},{"constant":"header__emphasis--style-italic"}]}]}'); 22 | let element = new Element(new Tagname(attrValues.constant("span")), [new Attribute("class", value)]); 23 | assert.deepEqual(element.toString(), ''); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --require source-map-support/register 3 | --inline-diffs 4 | 5 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "baseUrl": "dist", 6 | "types": [ 7 | "@types/chai", 8 | "@types/node", 9 | "@types/mocha" 10 | ] 11 | }, 12 | "include": [ 13 | "src", 14 | "test" 15 | ], 16 | "exclude": [ 17 | "dist", 18 | "node_modules" 19 | ], 20 | "references": [ 21 | {"path": "../code-style"} 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/tslint.cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.cli.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@opticss/code-style/configs/tslint.interactive.json" 3 | } -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/tslint.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.release.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/element-analysis/yarn-error.log: -------------------------------------------------------------------------------- 1 | Arguments: 2 | /Users/ceppstei/.nvm/versions/node/v8.1.3/bin/node /Users/ceppstei/.nvm/versions/node/v8.1.3/bin/yarn install 3 | 4 | PATH: 5 | /Users/ceppstei/.nvm/versions/node/v8.1.3/bin:/Users/ceppstei/.rbenv/shims:/Users/ceppstei/.rbenv/bin:/usr/local/linkedin/bin:/opt/subversion/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/ceppstei/bin:/Users/ceppstei/.rvm/bin 6 | 7 | Yarn version: 8 | 1.1.0 9 | 10 | Node version: 11 | 8.1.3 12 | 13 | Platform: 14 | darwin x64 15 | 16 | npm manifest: 17 | { 18 | "name": "@opticss/element-analysis", 19 | "version": "0.1.0", 20 | "description": "Data structures for storing the analysis of elements.", 21 | "main": "dist/src/index.js", 22 | "types": "dist/src/index.d.ts", 23 | "scripts": { 24 | "env": "env", 25 | "compile": "rm -rf dist test/build && tsc", 26 | "pretest": "yarn run compile && yarn run tslint", 27 | "prepublish": "rm -rf dist && tsc", 28 | "tslint": "tslint --project tsconfig.json", 29 | "test": "mocha test/build/test --opts test/mocha.opts", 30 | "coverage": "istanbul cover -i \"dist/src/**/*.js\" --dir ./build/coverage node_modules/.bin/_mocha -- dist/test --opts test/mocha.opts", 31 | "remap": "remap-istanbul -i build/coverage/coverage.json -o coverage -t html", 32 | "docs": "typedoc --out ./docs ." 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git+https://github.com/linkedin/opticss.git" 37 | }, 38 | "keywords": [ 39 | "css", 40 | "cascade" 41 | ], 42 | "author": "Chris Eppstein", 43 | "license": "BSD-2-Clause", 44 | "bugs": { 45 | "url": "https://github.com/linkedin/opticss/issues" 46 | }, 47 | "homepage": "https://github.com/linkedin/opticss#readme", 48 | "peerDependencies": { 49 | "opticss": "*" 50 | }, 51 | "dependencies": { 52 | "@opticss/util": "*", 53 | }, 54 | "devDependencies": { 55 | "@types/node": "^8.0.26", 56 | "mocha": "^3.4.2", 57 | "mocha-typescript": "^1.1.9", 58 | "tslint": "^5.10.0", 59 | "typescript": "^2.4.1" 60 | } 61 | } 62 | 63 | yarn manifest: 64 | No manifest 65 | 66 | Lockfile: 67 | No lockfile 68 | 69 | Trace: 70 | SyntaxError: /Users/ceppstei/Work/sailfish/opticss/packages/element-analysis/package.json: Unexpected token } in JSON at position 1191 71 | at JSON.parse () 72 | at /Users/ceppstei/.nvm/versions/node/v8.1.3/lib/node_modules/yarn/lib/cli.js:764:59 73 | at Generator.next () 74 | at step (/Users/ceppstei/.nvm/versions/node/v8.1.3/lib/node_modules/yarn/lib/cli.js:92:30) 75 | at /Users/ceppstei/.nvm/versions/node/v8.1.3/lib/node_modules/yarn/lib/cli.js:103:13 76 | at 77 | -------------------------------------------------------------------------------- /packages/@opticss/simple-template/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | test/ 3 | ts*.json 4 | -------------------------------------------------------------------------------- /packages/@opticss/simple-template/.vscode/cSpell.json: -------------------------------------------------------------------------------- 1 | // cSpell Settings 2 | { 3 | // Version of the setting file. Always 0.1 4 | "version": "0.1", 5 | // language - current active spelling language 6 | "language": "en", 7 | // words - list of words to be always considered correct 8 | "words": [ 9 | "Rewriteable", 10 | "opticss" 11 | ], 12 | // flagWords - list of words to be always considered incorrect 13 | // This is useful for offensive words and common spelling errors. 14 | // For example "hte" should be "the" 15 | "flagWords": [] 16 | } -------------------------------------------------------------------------------- /packages/@opticss/simple-template/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "preLaunchTask": "compile", 12 | "program": "${workspaceRoot}/node_modules/.bin/_mocha", 13 | "args": [ 14 | "dist/test", 15 | "--opts", 16 | "test/mocha.opts" 17 | ], 18 | "cwd": "${workspaceRoot}", 19 | "outFiles": [ 20 | "${workspaceRoot}/dist/**/*.js" 21 | ], 22 | "sourceMaps": true 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/@opticss/simple-template/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "**/dist/**/*": true, 5 | "**/*.lock": true 6 | }, 7 | "editor.tabSize": 2, 8 | "typescriptHero.codeOutline.enabled": true, 9 | "typescriptHero.resolver.multiLineWrapThreshold": 10, 10 | "typescriptHero.resolver.multiLineTrailingComma": true, 11 | "typescriptHero.resolver.organizeOnSave": true, 12 | "tslint.configFile": "tslint.json", 13 | "tslint.enable": true, 14 | "cSpell.words": [ 15 | "Rewriteable" 16 | ], 17 | "tslint.packageManager": "yarn", 18 | "npm.packageManager": "yarn" 19 | } -------------------------------------------------------------------------------- /packages/@opticss/simple-template/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "compile", 8 | "type": "npm", 9 | "script": "compile", 10 | "problemMatcher": [] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /packages/@opticss/simple-template/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.6.4](https://github.com/linkedin/opticss/compare/@opticss/simple-template@0.6.3...@opticss/simple-template@0.6.4) (2020-08-04) 7 | 8 | **Note:** Version bump only for package @opticss/simple-template 9 | 10 | 11 | 12 | 13 | 14 | ## [0.6.3](https://github.com/linkedin/opticss/compare/@opticss/simple-template@0.6.2...@opticss/simple-template@0.6.3) (2019-09-16) 15 | 16 | **Note:** Version bump only for package @opticss/simple-template 17 | 18 | 19 | 20 | 21 | 22 | ## [0.6.2](https://github.com/linkedin/opticss/compare/@opticss/simple-template@0.6.1...@opticss/simple-template@0.6.2) (2019-05-02) 23 | 24 | **Note:** Version bump only for package @opticss/simple-template 25 | 26 | 27 | 28 | 29 | 30 | ## [0.6.1](https://github.com/linkedin/opticss/compare/@opticss/simple-template@0.6.0...@opticss/simple-template@0.6.1) (2019-04-30) 31 | 32 | 33 | ### Bug Fixes 34 | 35 | * Upgrade parse5 to 5.0.0 to enable browser builds. ([6a88a33](https://github.com/linkedin/opticss/commit/6a88a33)) 36 | 37 | 38 | 39 | 40 | 41 | # [0.6.0](https://github.com/linkedin/opticss/compare/@opticss/simple-template@0.5.0...@opticss/simple-template@0.6.0) (2019-04-25) 42 | 43 | **Note:** Version bump only for package @opticss/simple-template 44 | 45 | 46 | 47 | 48 | 49 | # 0.5.0 (2019-04-19) 50 | 51 | 52 | 53 | # 0.4.0 (2018-10-19) 54 | 55 | 56 | ### Features 57 | 58 | * Manually throw error for Node 6 in Optimizer. ([c4db789](https://github.com/linkedin/opticss/commit/c4db789)) 59 | 60 | 61 | 62 | # 0.3.0 (2018-04-24) 63 | 64 | 65 | 66 | # 0.3.0-rc.0 (2018-04-24) 67 | 68 | 69 | 70 | 71 | 72 | 73 | # [0.3.0](https://github.com/linkedin/opticss/compare/v0.3.0-rc.0...v0.3.0) (2018-04-24) 74 | 75 | **Note:** Version bump only for package @opticss/simple-template 76 | 77 | 78 | 79 | 80 | 81 | 82 | # [0.2.0](https://github.com/linkedin/opticss/compare/v0.1.1...v0.2.0) (2017-11-26) 83 | 84 | 85 | 86 | 87 | **Note:** Version bump only for package @opticss/simple-template 88 | -------------------------------------------------------------------------------- /packages/@opticss/simple-template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opticss/simple-template", 3 | "version": "0.6.4", 4 | "description": "Simple templates for use in testing with OptiCSS.", 5 | "main": "dist/src/index.js", 6 | "types": "dist/src/index.d.ts", 7 | "scripts": { 8 | "compile": " tsc --build", 9 | "pretest": "yarn run compile", 10 | "test": "mocha dist/test --opts test/mocha.opts", 11 | "posttest": "yarn run lint", 12 | "prepublishOnly": "yarn run compile", 13 | "lint": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json", 14 | "lintall": "tslint -t msbuild --project tsconfig.json -c tslint.release.json", 15 | "lintfix": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json --fix", 16 | "coverage": "istanbul cover -i \"dist/src/**/*.js\" --dir ./build/coverage _mocha -- dist/test --opts test/mocha.opts && yarn run remap", 17 | "remap": "remap-istanbul -i build/coverage/coverage.json -o coverage -t html", 18 | "docs": "typedoc --out ./docs ." 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/linkedin/opticss.git" 23 | }, 24 | "keywords": [ 25 | "css", 26 | "cascade" 27 | ], 28 | "author": "Chris Eppstein", 29 | "license": "BSD-2-Clause", 30 | "bugs": { 31 | "url": "https://github.com/linkedin/opticss/issues?q=is%3Aopen+is%3Aissue+label%3Apkg%3Asimple-template" 32 | }, 33 | "homepage": "https://github.com/linkedin/opticss/tree/master/packages/%40opticss/simple-template", 34 | "engines": { 35 | "node": "6.* || 8.* || >= 10.*" 36 | }, 37 | "publishConfig": { 38 | "access": "public" 39 | }, 40 | "dependencies": { 41 | "@opticss/attr-analysis-dsl": "^0.6.3", 42 | "@opticss/element-analysis": "^0.6.2", 43 | "@opticss/template-api": "^0.7.0", 44 | "@opticss/util": "^0.7.0", 45 | "@types/random-js": "^1.0.30", 46 | "parse5": "^5.0.0", 47 | "parse5-sax-parser": "^5.0.0", 48 | "postcss": "^7.0.14", 49 | "random-js": "^2.0.0", 50 | "specificity": "^0.4.1" 51 | }, 52 | "devDependencies": { 53 | "@opticss/code-style": "^0.6.0", 54 | "@types/chai": "^4.0.4", 55 | "@types/mocha": "^5.2.6", 56 | "@types/parse5": "^5.0.0", 57 | "@types/parse5-sax-parser": "^5.0.1", 58 | "chai": "^4.1.2", 59 | "istanbul": "^0.4.5", 60 | "mocha": "^6.1.4", 61 | "mocha-typescript": "^1.1.9", 62 | "remap-istanbul": "^0.13.0", 63 | "source-map-support": "^0.5.3", 64 | "tslint": "^5.10.0", 65 | "typedoc": "^0.15.0-0", 66 | "typescript": "~3.4.4" 67 | }, 68 | "gitHead": "ef310cb1b10dbc90cae4f859da146863f99d940b", 69 | "toolchain": { 70 | "node": "10.15.3", 71 | "yarn": "1.15.2" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /packages/@opticss/simple-template/src/SimpleAnalyzer.ts: -------------------------------------------------------------------------------- 1 | import { AttributeValueParser } from "@opticss/attr-analysis-dsl"; 2 | import { Attribute, AttributeNS, POSITION_UNKNOWN, Tagname } from "@opticss/element-analysis"; 3 | import { TemplateAnalysis } from "@opticss/template-api"; 4 | import * as parse5 from "parse5-sax-parser"; 5 | 6 | import { TestTemplate } from "./TestTemplate"; 7 | 8 | export class SimpleAnalyzer { 9 | template: TestTemplate; 10 | includeSourceInformation: boolean; 11 | private valueParser: AttributeValueParser; 12 | 13 | /** 14 | * Creates an instance of SimpleAnalyzer. 15 | * @param template The template to be analyzed. 16 | * @param [includeSourceInformation=false] Whether to record source positions 17 | * for elements in the analysis. 18 | */ 19 | constructor(template: TestTemplate, includeSourceInformation = false) { 20 | this.template = template; 21 | this.includeSourceInformation = includeSourceInformation; 22 | this.valueParser = new AttributeValueParser(template.plainHtml); 23 | } 24 | 25 | /** 26 | * Analyze the TestTemplate. 27 | * @returns A promise that resolves with the analysis object after parsing. 28 | */ 29 | analyze(): Promise> { 30 | let analysis = new TemplateAnalysis<"TestTemplate">(this.template); 31 | 32 | // @ts-ignore 33 | // Typescript doesn't like considering the default export a constructor... 34 | const parser = new parse5({ sourceCodeLocationInfo: this.includeSourceInformation }); 35 | 36 | // On every element start tag, parse all attributes and add to the analysis. 37 | parser.on("startTag", (token: parse5.StartTagToken) => { 38 | let { tagName, attrs, sourceCodeLocation: location } = token; 39 | let startLocation = location ? { line: location.startLine, column: location.startCol } : POSITION_UNKNOWN; 40 | let endLocation = location ? { line: location.startLine, column: location.startCol + location.endOffset } : POSITION_UNKNOWN; 41 | analysis.startElement(new Tagname({constant: tagName}), startLocation); 42 | attrs.forEach(attr => { 43 | let attrValue = this.valueParser.parse(attr.namespace, attr.name, attr.value); 44 | if (attr.namespace) { 45 | analysis.addAttribute(new AttributeNS(attr.namespace, attr.name, attrValue)); 46 | } else { 47 | analysis.addAttribute(new Attribute(attr.name, attrValue)); 48 | } 49 | }); 50 | analysis.endElement(endLocation); 51 | }); 52 | 53 | // Return a promise that resolves with the analysis object after parsing and analysis. 54 | return new Promise((resolve, reject) => { 55 | parser.write(this.template.contents, (err: unknown) => { 56 | if (err) { reject(err); } 57 | else { resolve(analysis); } 58 | }); 59 | }); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/@opticss/simple-template/src/TestTemplate.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SerializedTemplateInfo, 3 | TemplateInfo, 4 | TemplateInfoFactory, 5 | } from "@opticss/template-api"; 6 | 7 | declare module "@opticss/template-api" { 8 | export interface TemplateTypes { 9 | "TestTemplate": TestTemplate; 10 | } 11 | } 12 | 13 | /** 14 | * Represents a single TestTemplate and its associated content and meta data. 15 | */ 16 | export class TestTemplate implements TemplateInfo<"TestTemplate"> { 17 | type: "TestTemplate"; 18 | identifier: string; 19 | contents: string; 20 | plainHtml: boolean; 21 | 22 | /** 23 | * Creates an instance of TestTemplate. 24 | * @param identifier Unique identifier for this template. Typically a filepath. 25 | * @param contents The string contents of this template file. 26 | * @param plainHtml Boolean, if this file is plain HTML, or contains dynamic attributes. 27 | */ 28 | constructor(identifier: string, contents: string, plainHtml?: boolean) { 29 | this.type = "TestTemplate"; 30 | this.identifier = identifier; 31 | this.contents = contents; 32 | this.plainHtml = !!plainHtml; 33 | } 34 | 35 | static deserialize(identifier: string, ...data: string[]): TestTemplate { 36 | return new TestTemplate(identifier, data[0], data[1] === "true"); 37 | } 38 | 39 | serialize(): SerializedTemplateInfo<"TestTemplate"> { 40 | return { 41 | type: "TestTemplate", 42 | identifier: this.identifier, 43 | data: [ 44 | this.contents, 45 | this.plainHtml ? "true" : "false", 46 | ], 47 | }; 48 | } 49 | } 50 | 51 | // Register this template type on our TemplateFactory. 52 | TemplateInfoFactory.constructors["TestTemplate"] = TestTemplate.deserialize; 53 | -------------------------------------------------------------------------------- /packages/@opticss/simple-template/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./SimpleAnalyzer"; 2 | export * from "./SimpleTemplateRewriter"; 3 | export * from "./SimpleTemplateRunner"; 4 | export * from "./TestTemplate"; 5 | -------------------------------------------------------------------------------- /packages/@opticss/simple-template/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --require source-map-support/register 3 | --inline-diffs 4 | 5 | -------------------------------------------------------------------------------- /packages/@opticss/simple-template/test/simple-template-runner-test.ts: -------------------------------------------------------------------------------- 1 | import { clean } from "@opticss/util"; 2 | import { 3 | assert, 4 | } from "chai"; 5 | import { 6 | suite, 7 | test, 8 | } from "mocha-typescript"; 9 | 10 | import { 11 | SimpleTemplateRunner, 12 | TestTemplate, 13 | } from "../src"; 14 | 15 | @suite("Simple Template Runner") 16 | export class SimpleTemplateTest { 17 | @test "Can run once"() { 18 | let template = new TestTemplate("test.tmpl", `
`); 19 | let runner = new SimpleTemplateRunner(template); 20 | return runner.runOnce().then(value => { 21 | assert.deepEqual(value, `
`); 22 | }); 23 | } 24 | @test "Can run once with an optional value"() { 25 | let template = new TestTemplate("test.tmpl", `
`); 26 | let runner = new SimpleTemplateRunner(template); 27 | return runner.runOnce().then(value => { 28 | assert.deepEqual(value, `
`); 29 | }); 30 | } 31 | @test "Can run all with an optional value"() { 32 | let template = new TestTemplate("test.tmpl", `
`); 33 | let runner = new SimpleTemplateRunner(template); 34 | return runner.runAll().then(value => { 35 | assert.deepEqual(value[0], `
`); 36 | assert.deepEqual(value[1], `
`); 37 | }); 38 | } 39 | @test "Can run all with an optional value 2"() { 40 | let template = new TestTemplate("test.tmpl", clean` 41 |
ORANGE
42 |
APPLE
43 |
STRAWBERRY
`); 44 | let runner = new SimpleTemplateRunner(template); 45 | return runner.runAll().then(value => { 46 | assert.deepEqual(value[0], clean`
ORANGE
47 |
APPLE
48 |
STRAWBERRY
`); 49 | assert.deepEqual(value[1], clean`
ORANGE
50 |
APPLE
51 |
STRAWBERRY
`); 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/@opticss/simple-template/test/simple-template-test.ts: -------------------------------------------------------------------------------- 1 | // import { 2 | // assert, 3 | // } from 'chai'; 4 | import { 5 | suite, 6 | test, 7 | } from "mocha-typescript"; 8 | 9 | // import clean from './util/clean'; 10 | 11 | @suite("Simple Templates") 12 | export class SimpleTemplateTest { 13 | @test "Can analyze"() { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/@opticss/simple-template/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "baseUrl": "dist", 6 | "types": [ 7 | "@types/chai", 8 | "@types/node", 9 | "@types/mocha" 10 | ], 11 | "paths": { 12 | "parse5": ["../types-local/parse5/index.d.ts"], 13 | "parse5-sax-parser": ["../types-local/parse5-sax-parser/index.d.ts"] 14 | } 15 | }, 16 | "include": [ 17 | "src", 18 | "test" 19 | ], 20 | "exclude": [ 21 | "dist", 22 | "node_modules" 23 | ], 24 | "references": [ 25 | {"path": "../attr-analysis-dsl"}, 26 | {"path": "../element-analysis"}, 27 | {"path": "../template-api"}, 28 | {"path": "../util"}, 29 | {"path": "../code-style"} 30 | ] 31 | } -------------------------------------------------------------------------------- /packages/@opticss/simple-template/tslint.cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.cli.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/simple-template/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@opticss/code-style/configs/tslint.interactive.json" 3 | } -------------------------------------------------------------------------------- /packages/@opticss/simple-template/tslint.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.release.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/template-api/.vscode/cSpell.json: -------------------------------------------------------------------------------- 1 | // cSpell Settings 2 | { 3 | // Version of the setting file. Always 0.1 4 | "version": "0.1", 5 | // language - current active spelling language 6 | "language": "en", 7 | // words - list of words to be always considered correct 8 | "words": [ 9 | "Combinator", 10 | "Combinators", 11 | "Eppstein", 12 | "Opti", 13 | "Opticss", 14 | "Rewritable", 15 | "Rewriteable", 16 | "Selectable", 17 | "Selectorish", 18 | "asdf", 19 | "atrule", 20 | "brotli", 21 | "classname", 22 | "contenteditable", 23 | "deserialize", 24 | "deserializer", 25 | "eppsteins", 26 | "idents", 27 | "klass", 28 | "mergeable", 29 | "mergeables", 30 | "nearley", 31 | "optimizable", 32 | "optimizer's", 33 | "promisify", 34 | "pseudoelement", 35 | "pseudoelements", 36 | "selectables", 37 | "serializable", 38 | "shorthands", 39 | "sourcemap", 40 | "superset", 41 | "supersets" 42 | ], 43 | // flagWords - list of words to be always considered incorrect 44 | // This is useful for offensive words and common spelling errors. 45 | // For example "hte" should be "the" 46 | "flagWords": [ 47 | "hte" 48 | ] 49 | } -------------------------------------------------------------------------------- /packages/@opticss/template-api/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "preLaunchTask": "compile", 12 | "program": "${workspaceRoot}/node_modules/.bin/_mocha", 13 | "args": [ 14 | "test/build/test", 15 | "--opts", 16 | "test/mocha.opts" 17 | ], 18 | "cwd": "${workspaceRoot}", 19 | "outFiles": [ 20 | "${workspaceRoot}/dist/**/*.js" 21 | ], 22 | "sourceMaps": true 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/@opticss/template-api/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "**/dist/**/*": true, 5 | "**/*.lock": true 6 | }, 7 | "editor.tabSize": 2, 8 | "typescriptHero.codeOutline.enabled": true, 9 | "typescriptHero.resolver.multiLineWrapThreshold": 10, 10 | "typescriptHero.resolver.multiLineTrailingComma": true, 11 | "typescriptHero.resolver.organizeOnSave": true, 12 | "typescript.tsdk": "node_modules/typescript/lib", 13 | "tslint.enable": true, 14 | "tslint.configFile": "tslint.json", 15 | "typescript.useCodeSnippetsOnMethodSuggest": true, 16 | "typescriptHero.resolver.ignorePatterns": [ 17 | "dist", 18 | "node_modules", 19 | "test", 20 | "index.ts" 21 | ], 22 | "tslint.packageManager": "yarn", 23 | "npm.packageManager": "yarn" 24 | } -------------------------------------------------------------------------------- /packages/@opticss/template-api/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "compile", 8 | "type": "npm", 9 | "script": "compile", 10 | "problemMatcher": [] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /packages/@opticss/template-api/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.7.0](https://github.com/linkedin/opticss/compare/@opticss/template-api@0.6.3...@opticss/template-api@0.7.0) (2020-08-04) 7 | 8 | 9 | ### Features 10 | 11 | * Expose StyleMapping data as public. ([ab815be](https://github.com/linkedin/opticss/commit/ab815be)) 12 | * Record in the stylemapping styles used before optimization. ([314a8c9](https://github.com/linkedin/opticss/commit/314a8c9)) 13 | 14 | 15 | 16 | 17 | 18 | ## [0.6.3](https://github.com/linkedin/opticss/compare/@opticss/template-api@0.6.2...@opticss/template-api@0.6.3) (2019-09-16) 19 | 20 | **Note:** Version bump only for package @opticss/template-api 21 | 22 | 23 | 24 | 25 | 26 | ## [0.6.2](https://github.com/linkedin/opticss/compare/@opticss/template-api@0.6.1...@opticss/template-api@0.6.2) (2019-05-02) 27 | 28 | **Note:** Version bump only for package @opticss/template-api 29 | 30 | 31 | 32 | 33 | 34 | ## [0.6.1](https://github.com/linkedin/opticss/compare/@opticss/template-api@0.6.0...@opticss/template-api@0.6.1) (2019-04-30) 35 | 36 | **Note:** Version bump only for package @opticss/template-api 37 | 38 | 39 | 40 | 41 | 42 | # [0.6.0](https://github.com/linkedin/opticss/compare/@opticss/template-api@0.5.0...@opticss/template-api@0.6.0) (2019-04-25) 43 | 44 | **Note:** Version bump only for package @opticss/template-api 45 | 46 | 47 | 48 | 49 | 50 | # 0.5.0 (2019-04-19) 51 | 52 | 53 | 54 | # 0.4.0 (2018-10-19) 55 | 56 | 57 | ### Features 58 | 59 | * Manually throw error for Node 6 in Optimizer. ([c4db789](https://github.com/linkedin/opticss/commit/c4db789)) 60 | 61 | 62 | 63 | # 0.3.0 (2018-04-24) 64 | 65 | 66 | 67 | # 0.3.0-rc.0 (2018-04-24) 68 | 69 | 70 | 71 | 72 | 73 | 74 | # [0.3.0](https://github.com/linkedin/opticss/compare/v0.3.0-rc.0...v0.3.0) (2018-04-24) 75 | 76 | **Note:** Version bump only for package @opticss/template-api 77 | 78 | 79 | 80 | 81 | 82 | 83 | # [0.2.0](https://github.com/linkedin/opticss/compare/v0.1.1...v0.2.0) (2017-11-26) 84 | 85 | 86 | 87 | 88 | **Note:** Version bump only for package @opticss/template-api 89 | -------------------------------------------------------------------------------- /packages/@opticss/template-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opticss/template-api", 3 | "version": "0.7.0", 4 | "description": "Simple templates for use in testing with OptiCSS.", 5 | "main": "dist/src/index.js", 6 | "types": "dist/src/index.d.ts", 7 | "scripts": { 8 | "compile-src": "tsc --build", 9 | "compile-test": "rm -rf test/build && tsc --build tsconfig-test.json", 10 | "compile": "yarn run compile-src && yarn run compile-test", 11 | "pretest": "yarn run compile", 12 | "test": "mocha test/build/test --opts test/mocha.opts", 13 | "posttest": "yarn run lint", 14 | "prepublishOnly": "yarn run compile-src", 15 | "lint": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json && tslint -t msbuild --project tsconfig-test.json -c tslint.cli.json", 16 | "lintall": "tslint -t msbuild --project tsconfig.json -c tslint.release.json && tslint -t msbuild --project tsconfig-test.json -c tslint.release.json", 17 | "lintfix": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json --fix && tslint -t msbuild --project tsconfig-test.json -c tslint.cli.json --fix", 18 | "coverage": "istanbul cover -i \"dist/src/**/*.js\" --dir ./build/coverage _mocha -- dist/test --opts test/mocha.opts", 19 | "remap": "remap-istanbul -i build/coverage/coverage.json -o coverage -t html", 20 | "docs": "typedoc --out ./docs ." 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/linkedin/opticss.git" 25 | }, 26 | "keywords": [ 27 | "css", 28 | "cascade" 29 | ], 30 | "author": "Chris Eppstein", 31 | "license": "BSD-2-Clause", 32 | "bugs": { 33 | "url": "https://github.com/linkedin/opticss/issues?q=is%3Aopen+is%3Aissue+label%3Apkg%3Atemplate-api" 34 | }, 35 | "homepage": "https://github.com/linkedin/opticss/tree/master/packages/%40opticss/template-api", 36 | "engines": { 37 | "node": "6.* || 8.* || >= 10.*" 38 | }, 39 | "publishConfig": { 40 | "access": "public" 41 | }, 42 | "dependencies": { 43 | "@opticss/element-analysis": "^0.6.2", 44 | "@opticss/util": "^0.7.0", 45 | "postcss": "^7.0.14", 46 | "typescript-collections": "^1.2.5" 47 | }, 48 | "devDependencies": { 49 | "@opticss/attr-analysis-dsl": "^0.6.3", 50 | "@opticss/code-style": "^0.6.0", 51 | "@types/chai": "^4.0.4", 52 | "@types/mocha": "^5.2.6", 53 | "chai": "^4.1.2", 54 | "istanbul": "^0.4.5", 55 | "mocha": "^6.1.4", 56 | "mocha-typescript": "^1.1.9", 57 | "remap-istanbul": "^0.13.0", 58 | "source-map-support": "^0.5.3", 59 | "tslint": "^5.10.0", 60 | "typedoc": "^0.15.0-0", 61 | "typescript": "~3.4.4" 62 | }, 63 | "gitHead": "ef310cb1b10dbc90cae4f859da146863f99d940b", 64 | "toolchain": { 65 | "node": "10.15.3", 66 | "yarn": "1.15.2" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/@opticss/template-api/src/TemplateError.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ErrorLocation, 3 | OptiCSSError, 4 | } from "@opticss/util"; 5 | 6 | /** 7 | * Custom OptiCSS error type for template analysis errors. 8 | */ 9 | export class TemplateError extends OptiCSSError { 10 | static prefix = "TemplateError"; 11 | constructor(message: string, location?: ErrorLocation) { 12 | super(message, location); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/@opticss/template-api/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --require source-map-support/register 3 | --inline-diffs 4 | 5 | -------------------------------------------------------------------------------- /packages/@opticss/template-api/test/template-api.ts: -------------------------------------------------------------------------------- 1 | import { BooleanExpression, and, not, or } from "@opticss/template-api"; 2 | import { assert } from "chai"; 3 | import { suite, test } from "mocha-typescript"; 4 | 5 | @suite("Template API") 6 | export class TemplateAPITest { 7 | @test "Boolean expressions"() { 8 | let boolExpr: BooleanExpression = and(1, or(2, 3), not(4)); 9 | assert.deepEqual(boolExpr, { 10 | and: [ 11 | 1, 12 | {or: [2, 3]}, 13 | {not: 4}, 14 | ], 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/@opticss/template-api/tsconfig-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "test/build", 5 | "baseUrl": "test/build", 6 | "types": [ 7 | "@types/chai", 8 | "@types/node", 9 | "@types/mocha" 10 | ] 11 | }, 12 | "include": [ 13 | "test" 14 | ], 15 | "exclude": [ 16 | "dist", 17 | "test/build", 18 | "node_modules" 19 | ], 20 | "references": [ 21 | {"path": "tsconfig.json"} 22 | ] 23 | } -------------------------------------------------------------------------------- /packages/@opticss/template-api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "baseUrl": "dist", 6 | "types": [ 7 | "@types/node" 8 | ] 9 | }, 10 | "include": [ 11 | "src" 12 | ], 13 | "exclude": [ 14 | "dist", 15 | "test/build", 16 | "node_modules" 17 | ], 18 | "references": [ 19 | {"path": "../attr-analysis-dsl"}, 20 | {"path": "../code-style"} 21 | ] 22 | } -------------------------------------------------------------------------------- /packages/@opticss/template-api/tslint.cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.cli.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/template-api/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@opticss/code-style/configs/tslint.interactive.json" 3 | } -------------------------------------------------------------------------------- /packages/@opticss/template-api/tslint.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.release.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/util/.vscode/cSpell.json: -------------------------------------------------------------------------------- 1 | // cSpell Settings 2 | { 3 | // Version of the setting file. Always 0.1 4 | "version": "0.1", 5 | // language - current active spelling language 6 | "language": "en", 7 | // words - list of words to be always considered correct 8 | "words": [ 9 | "asdf", 10 | "atrule", 11 | "brotli", 12 | "classname", 13 | "Combinator", 14 | "Combinators", 15 | "contenteditable", 16 | "deserialize", 17 | "Eppstein", 18 | "eppsteins", 19 | "idents", 20 | "klass", 21 | "mergeable", 22 | "mergeables", 23 | "nearley", 24 | "Opti", 25 | "Opticss", 26 | "optimizable", 27 | "optimizer's", 28 | "promisify", 29 | "pseudoelement", 30 | "pseudoelements", 31 | "Rewritable", 32 | "Selectable", 33 | "selectables", 34 | "Selectorish", 35 | "shorthands", 36 | "sourcemap", 37 | "stylesheet", 38 | "superset", 39 | "supersets", 40 | "unoptimized" 41 | ], 42 | // flagWords - list of words to be always considered incorrect 43 | // This is useful for offensive words and common spelling errors. 44 | // For example "hte" should be "the" 45 | "flagWords": [ 46 | "hte" 47 | ] 48 | } -------------------------------------------------------------------------------- /packages/@opticss/util/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "preLaunchTask": "compile", 12 | "program": "${workspaceRoot}/node_modules/.bin/_mocha", 13 | "args": [ 14 | "dist/test", 15 | "--opts", 16 | "test/mocha.opts" 17 | ], 18 | "cwd": "${workspaceRoot}", 19 | "outFiles": [ 20 | "${workspaceRoot}/dist/**/*.js" 21 | ], 22 | "sourceMaps": true 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/@opticss/util/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "**/dist/**/*": true, 5 | "**/*.lock": true 6 | }, 7 | "editor.tabSize": 2, 8 | "typescriptHero.codeOutline.enabled": true, 9 | "typescriptHero.resolver.multiLineWrapThreshold": 10, 10 | "typescriptHero.resolver.multiLineTrailingComma": true, 11 | "typescriptHero.resolver.organizeOnSave": true, 12 | "typescript.tsdk": "node_modules/typescript/lib", 13 | "tslint.enable": true, 14 | "tslint.configFile": "tslint.json", 15 | "typescript.useCodeSnippetsOnMethodSuggest": true, 16 | "tslint.packageManager": "yarn", 17 | "npm.packageManager": "yarn" 18 | } -------------------------------------------------------------------------------- /packages/@opticss/util/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "compile", 8 | "type": "npm", 9 | "script": "compile", 10 | "problemMatcher": [] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /packages/@opticss/util/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.7.0](https://github.com/linkedin/opticss/compare/@opticss/util@0.6.1...@opticss/util@0.7.0) (2019-09-16) 7 | 8 | 9 | ### Features 10 | 11 | * Remove method handling from Maybe. ([f208dbf](https://github.com/linkedin/opticss/commit/f208dbf)) 12 | 13 | 14 | 15 | 16 | 17 | ## [0.6.1](https://github.com/linkedin/opticss/compare/@opticss/util@0.6.0...@opticss/util@0.6.1) (2019-04-30) 18 | 19 | **Note:** Version bump only for package @opticss/util 20 | 21 | 22 | 23 | 24 | 25 | # [0.6.0](https://github.com/linkedin/opticss/compare/@opticss/util@0.5.0...@opticss/util@0.6.0) (2019-04-25) 26 | 27 | 28 | ### Features 29 | 30 | * Remove whatever in favor of builtin unknown. ([f794d80](https://github.com/linkedin/opticss/commit/f794d80)) 31 | 32 | 33 | ### BREAKING CHANGES 34 | 35 | * The `whatever` type is functionally equivalent to the TS 36 | `unknown` type. Code that was using `whatever` should be changed to use 37 | TypeScript's built-in type `unknown` instead. 38 | 39 | 40 | 41 | 42 | 43 | # 0.5.0 (2019-04-19) 44 | 45 | 46 | ### Bug Fixes 47 | 48 | * Fix type failures for typesafe flatten utility. ([4853b3f](https://github.com/linkedin/opticss/commit/4853b3f)) 49 | 50 | 51 | 52 | # 0.4.0 (2018-10-19) 53 | 54 | 55 | ### Features 56 | 57 | * Add function to flatten an array with strong type checking. ([f104638](https://github.com/linkedin/opticss/commit/f104638)) 58 | * Manually throw error for Node 6 in Optimizer. ([c4db789](https://github.com/linkedin/opticss/commit/c4db789)) 59 | 60 | 61 | 62 | # 0.3.0 (2018-04-24) 63 | 64 | 65 | 66 | # 0.3.0-rc.0 (2018-04-24) 67 | 68 | 69 | 70 | 71 | 72 | 73 | # [0.3.0](https://github.com/linkedin/opticss/compare/v0.3.0-rc.0...v0.3.0) (2018-04-24) 74 | 75 | **Note:** Version bump only for package @opticss/util 76 | -------------------------------------------------------------------------------- /packages/@opticss/util/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opticss/util", 3 | "version": "0.7.0", 4 | "description": "Utility library for misc shared code for @opticss packages.", 5 | "main": "dist/src/index.js", 6 | "types": "dist/src/index.d.ts", 7 | "scripts": { 8 | "compile": "tsc --build", 9 | "watch": "tsc --build --watch", 10 | "pretest": "yarn run compile", 11 | "test": "mocha dist/test --opts test/mocha.opts", 12 | "posttest": "yarn run lint", 13 | "prepublishOnly": "yarn run compile && yarn run lintall", 14 | "lint": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json", 15 | "lintall": "tslint -t msbuild --project tsconfig.json -c tslint.release.json", 16 | "lintfix": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json --fix", 17 | "coverage": "istanbul cover -i \"dist/src/**/*.js\" --dir ./build/coverage _mocha -- dist/test --opts test/mocha.opts", 18 | "remap": "remap-istanbul -i build/coverage/coverage.json -o coverage -t html", 19 | "docs": "typedoc --out ./docs ." 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/linkedin/opticss.git" 24 | }, 25 | "keywords": [ 26 | "css", 27 | "cascade", 28 | "typescript", 29 | "maybe", 30 | "datastructures" 31 | ], 32 | "author": "Chris Eppstein", 33 | "license": "BSD-2-Clause", 34 | "bugs": { 35 | "url": "https://github.com/linkedin/opticss/issues?q=is%3Aopen+is%3Aissue+label%3Apkg%3Autil" 36 | }, 37 | "homepage": "https://github.com/linkedin/opticss/tree/master/packages/%40opticss/util", 38 | "engines": { 39 | "node": "6.* || 8.* || >= 10.*" 40 | }, 41 | "publishConfig": { 42 | "access": "public" 43 | }, 44 | "dependencies": { 45 | "typescript-collections": "^1.2.5" 46 | }, 47 | "devDependencies": { 48 | "@opticss/code-style": "^0.6.0", 49 | "@types/chai": "^4.0.4", 50 | "@types/mocha": "^5.2.6", 51 | "chai": "^4.1.2", 52 | "istanbul": "^0.4.5", 53 | "mocha": "^6.1.4", 54 | "mocha-typescript": "^1.1.9", 55 | "remap-istanbul": "^0.13.0", 56 | "source-map-support": "^0.5.3", 57 | "tslint": "^5.10.0", 58 | "typedoc": "^0.15.0-0", 59 | "typescript": "~3.4.4" 60 | }, 61 | "gitHead": "ef310cb1b10dbc90cae4f859da146863f99d940b", 62 | "toolchain": { 63 | "node": "10.15.3", 64 | "yarn": "1.15.2" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /packages/@opticss/util/src/OptiCSSError.ts: -------------------------------------------------------------------------------- 1 | export interface ErrorLocation { 2 | filename?: string; 3 | line?: number; 4 | column?: number; 5 | } 6 | 7 | /** 8 | * Custom Opticss error base class. Will format `ErrorLocation` into thrown 9 | * error message if provided. 10 | */ 11 | export class OptiCSSError extends Error { 12 | static prefix = "Error"; 13 | origMessage: string; 14 | private _location?: ErrorLocation; 15 | constructor(message: string, location?: ErrorLocation) { 16 | super(message); 17 | this.origMessage = message; 18 | this._location = location; 19 | super.message = this.annotatedMessage(); 20 | } 21 | 22 | private annotatedMessage() { 23 | let loc = this.location; 24 | if (!loc) { 25 | return this.origMessage; 26 | } 27 | let filename = loc.filename || ""; 28 | let line = loc.line ? `:${loc.line}` : ""; 29 | let column = loc.column ? `:${loc.column}` : ""; 30 | let locMessage = ` (${filename}${line}${column})`; 31 | let prefix = (>this.constructor).prefix; 32 | return `OptiCSS ${prefix || OptiCSSError.prefix}: ${this.origMessage}${locMessage}`; 33 | } 34 | 35 | get location(): ErrorLocation | undefined { 36 | return this._location; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/@opticss/util/src/assertNever.ts: -------------------------------------------------------------------------------- 1 | import { inspect } from "util"; 2 | 3 | export function assertNever(value: never): never { 4 | throw new Error(`Unexpected value: ${inspect(value)}`); 5 | } 6 | 7 | export function assertNeverCalled(): never { 8 | throw new Error(`Unexpected invocation.`); 9 | } 10 | -------------------------------------------------------------------------------- /packages/@opticss/util/src/clean.ts: -------------------------------------------------------------------------------- 1 | export function clean(strings: TemplateStringsArray, ...expressions: string[]) { 2 | let str = strings.reduce( 3 | (prev, s, i) => 4 | prev + s + ((expressions.length > i) ? expressions[i].toString() : ""), 5 | ""); 6 | return str.split("\n").map(s => s.trim()).join("\n").trim(); 7 | } 8 | -------------------------------------------------------------------------------- /packages/@opticss/util/src/flatten.ts: -------------------------------------------------------------------------------- 1 | export type NestedArrayValue = V | NestedArray; 2 | export interface NestedArray extends Array> {} 3 | 4 | // tslint:disable-next-line:prefer-unknown-to-any 5 | function hasSubArray(a: Array): boolean { 6 | return a.some(i => Array.isArray(i)); 7 | } 8 | 9 | /** 10 | * returns an array where any array found within the specified array is 11 | * replaced by its contents recursively. 12 | * 13 | * @param array An infinitely nestable array of homogeneous data. 14 | */ 15 | export function flatten(array: NestedArray) { 16 | if (!hasSubArray(array)) { 17 | // avoid making a new array object and moving data to it unnecessarily 18 | return array; 19 | } else { 20 | let acc = new Array(); 21 | for (let val of array) { 22 | if (Array.isArray(val)) { 23 | acc = acc.concat(flatten(val)); 24 | } else { 25 | acc.push(val); 26 | } 27 | } 28 | return acc; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/@opticss/util/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as typedAssert from "./typedAssert"; 2 | 3 | export { typedAssert as assert }; 4 | 5 | export * from "./assertNever"; 6 | export * from "./clean"; 7 | export * from "./IdentityDictionary"; 8 | export * from "./UtilityTypes"; 9 | export * from "./Maybe"; 10 | export * from "./OptiCSSError"; 11 | export * from "./MultiMap"; 12 | export * from "./TwoKeyMultiMap"; 13 | export * from "./unionInto"; 14 | export * from "./flatten"; 15 | -------------------------------------------------------------------------------- /packages/@opticss/util/src/typedAssert.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | 3 | export function isDefined(value: X | undefined): {and: (cb: (defValue: X) => unknown) => void } { 4 | if (value) { 5 | return { 6 | and: function(cb: (v: X) => void) { 7 | cb(value); 8 | }, 9 | }; 10 | } else { 11 | assert(value !== undefined, `expected to be defined`); 12 | throw new Error("this is unreachable"); 13 | } 14 | } 15 | 16 | export function isNotNull(value: X | null): {and: (cb: (defValue: X) => unknown) => void } { 17 | if (value) { 18 | return { 19 | and: function(cb: (v: X) => void) { 20 | cb(value); 21 | }, 22 | }; 23 | } else { 24 | assert(value !== null, `expected to not be null`); 25 | throw new Error("this is unreachable"); 26 | } 27 | } 28 | 29 | export function isExisting(value: X | null | undefined): {and: (cb: (defValue: X) => unknown) => void } { 30 | if (value) { 31 | return { 32 | and: function(cb: (v: X) => void) { 33 | cb(value); 34 | }, 35 | }; 36 | } else { 37 | assert(value, `expected to exist`); 38 | throw new Error("this is unreachable"); 39 | } 40 | } 41 | 42 | export function isType< 43 | ArgumentType, 44 | AssertedType extends ArgumentType 45 | >( 46 | typeGuard: (x: ArgumentType) => x is AssertedType, 47 | x: ArgumentType, 48 | 49 | ): { and: (cb: (x: AssertedType) => unknown) => void } { 50 | if (typeGuard(x)) { 51 | return { 52 | and: function(cb: (x: AssertedType) => void) { 53 | cb(x); 54 | }, 55 | }; 56 | } else { 57 | assert.fail(`is not the expected type`); 58 | throw new Error("this is unreachable"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/@opticss/util/src/unionInto.ts: -------------------------------------------------------------------------------- 1 | export function unionInto(target: Set, ...sets: Iterable[]) { 2 | for (let set of sets) { 3 | for (let v of set) { 4 | target.add(v); 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/@opticss/util/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --require source-map-support/register 3 | --inline-diffs 4 | 5 | -------------------------------------------------------------------------------- /packages/@opticss/util/test/utility-types-test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | } from "chai"; 4 | import { 5 | suite, 6 | test, 7 | } from "mocha-typescript"; 8 | 9 | import { 10 | ItemType, 11 | ObjectDictionary, 12 | firstOfType, 13 | objectDictionaryFromMap, 14 | } from "../src"; 15 | 16 | interface Association { 17 | key: Key; 18 | info: Value; 19 | } 20 | 21 | interface StringKeyInfo { 22 | keyIndexes: Array>; 23 | keyIsSet: Array>; 24 | } 25 | class StringKeys { 26 | private info: StringKeyInfo; 27 | keys: Array; 28 | constructor() { 29 | this.info = initializeStringKeyInfo(""); 30 | this.keys = [""]; 31 | } 32 | add(key: string) { 33 | let index = this.keys.length; 34 | this.info.keyIndexes.push({key, info: index}); 35 | this.info.keyIsSet.push({key, info: true}); 36 | } 37 | } 38 | 39 | function initializeStringKeyInfo(key: string): StringKeyInfo { 40 | let keyIndexesDefault: ItemType = {key, info: 0}; 41 | let keyIsSetDefault: ItemType = {key, info: true}; 42 | return { 43 | keyIndexes: [keyIndexesDefault], 44 | keyIsSet: [keyIsSetDefault], 45 | }; 46 | } 47 | 48 | interface FirstOfTypeA { 49 | type: "1A"; 50 | } 51 | interface FirstOfTypeB { 52 | type: "1B"; 53 | } 54 | 55 | type FirstOfTypes = FirstOfTypeA | FirstOfTypeB; 56 | 57 | function isTypeA(o: object): o is FirstOfTypeA { 58 | return (>o).type === "1A"; 59 | } 60 | 61 | @suite("Simple Templates") 62 | export class SimpleTemplateTest { 63 | @test "ItemType example"() { 64 | let foo = new StringKeys(); 65 | assert.deepEqual(foo, foo); 66 | } 67 | @test "firstOfType"() { 68 | let a = new Array(); 69 | let n = firstOfType(a, isTypeA); 70 | assert.isUndefined(n); 71 | a.push({type: "1B"}); 72 | a.push({type: "1B"}); 73 | a.push({type: "1A"}); 74 | a.push({type: "1B"}); 75 | n = firstOfType(a, isTypeA); 76 | assert.isDefined(n); 77 | assert.deepEqual(n, {type: "1A"}); 78 | } 79 | @test "objectDictionaryFromMap"() { 80 | let m = new Map(); 81 | m.set("c", 3); 82 | m.set("a", 1); 83 | m.set("b", 2); 84 | let d: ObjectDictionary = { 85 | a: 1, 86 | b: 2, 87 | c: 3, 88 | }; 89 | assert.deepEqual(objectDictionaryFromMap(m), d); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /packages/@opticss/util/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "baseUrl": "dist", 6 | "types": [ 7 | "@types/chai", 8 | "@types/node", 9 | "@types/mocha" 10 | ] 11 | }, 12 | "include": [ 13 | "src", 14 | "test" 15 | ], 16 | "exclude": [ 17 | "dist", 18 | "node_modules" 19 | ], 20 | "references": [ 21 | {"path": "../code-style"}, 22 | {"path": "../../resolve-cascade"} 23 | ] 24 | 25 | } 26 | -------------------------------------------------------------------------------- /packages/@opticss/util/tslint.cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.cli.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/@opticss/util/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@opticss/code-style/configs/tslint.interactive.json" 3 | } -------------------------------------------------------------------------------- /packages/@opticss/util/tslint.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.release.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/opticss/.vscode/cSpell.json: -------------------------------------------------------------------------------- 1 | // cSpell Settings 2 | { 3 | // Version of the setting file. Always 0.1 4 | "version": "0.1", 5 | // language - current active spelling language 6 | "language": "en", 7 | // words - list of words to be always considered correct 8 | "words": [ 9 | "Combinator", 10 | "Combinators", 11 | "Eppstein", 12 | "Opti", 13 | "Opticss", 14 | "Rewritable", 15 | "Selectable", 16 | "Selectorish", 17 | "Tagnames", 18 | "asdf", 19 | "atrule", 20 | "brotli", 21 | "classname", 22 | "contenteditable", 23 | "deserialize", 24 | "eppsteins", 25 | "idents", 26 | "klass", 27 | "mergeable", 28 | "mergeables", 29 | "nearley", 30 | "optimizable", 31 | "optimizer's", 32 | "promisify", 33 | "pseudoelement", 34 | "pseudoelements", 35 | "rewriteable", 36 | "selectables", 37 | "shorthands", 38 | "sourcemap", 39 | "superset", 40 | "supersets" 41 | ], 42 | // flagWords - list of words to be always considered incorrect 43 | // This is useful for offensive words and common spelling errors. 44 | // For example "hte" should be "the" 45 | "flagWords": [ 46 | "hte" 47 | ] 48 | } -------------------------------------------------------------------------------- /packages/opticss/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "preLaunchTask": "compile", 12 | "program": "${workspaceRoot}/../../node_modules/.bin/_mocha", 13 | "args": [ 14 | "dist/test", 15 | "--opts", 16 | "test/mocha.opts" 17 | ], 18 | "cwd": "${workspaceRoot}", 19 | "outFiles": [ 20 | "${workspaceRoot}/dist/**/*.js" 21 | ], 22 | "sourceMaps": true 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/opticss/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "**/dist/**/*": true, 5 | "**/*.lock": true 6 | }, 7 | "editor.tabSize": 2, 8 | "typescriptHero.codeOutline.enabled": true, 9 | "typescriptHero.resolver.multiLineWrapThreshold": 10, 10 | "typescriptHero.resolver.multiLineTrailingComma": true, 11 | "typescriptHero.resolver.organizeOnSave": true, 12 | "typescript.tsdk": "node_modules/typescript/lib", 13 | "tslint.enable": true, 14 | "tslint.configFile": "tslint.json", 15 | "typescript.useCodeSnippetsOnMethodSuggest": true, 16 | "cSpell.words": [ 17 | "Tagnames", 18 | "rewriteable" 19 | ] 20 | } -------------------------------------------------------------------------------- /packages/opticss/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "compile", 8 | "type": "npm", 9 | "script": "compile", 10 | "problemMatcher": ["$tsc"] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /packages/opticss/ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | # Architecture 2 | 3 | Opticss is a multi-pass CSS optimizer. -------------------------------------------------------------------------------- /packages/opticss/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "opticss", 3 | "version": "0.9.0", 4 | "description": "A CSS Optimizer", 5 | "main": "dist/src/index.js", 6 | "scripts": { 7 | "compile": "tsc --build", 8 | "pretest": "yarn run compile", 9 | "posttest": "yarn run lint", 10 | "prepublishOnly": "yarn run compile && yarn run lintall", 11 | "lint": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json", 12 | "lintall": "tslint -t msbuild --project tsconfig.json -c tslint.release.json", 13 | "lintfix": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json --fix", 14 | "test": "mocha dist/test/**/*.js --opts test/mocha.opts -i --grep Integration", 15 | "integration": "yarn run pretest && mocha dist/test --opts test/mocha.opts --grep Integration", 16 | "coverage": "istanbul cover -i \"dist/src/**/*.js\" --dir ./build/coverage _mocha -- dist/test --opts test/mocha.opts", 17 | "remap": "remap-istanbul -i build/coverage/coverage.json -o coverage -t html", 18 | "docs": "typedoc --out ./docs ." 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/linkedin/opticss.git" 23 | }, 24 | "keywords": [ 25 | "css", 26 | "optimizer" 27 | ], 28 | "author": "Chris Eppstein", 29 | "license": "BSD-2-Clause", 30 | "bugs": { 31 | "url": "https://github.com/linkedin/opticss/issues" 32 | }, 33 | "types": "dist/src/index.d.ts", 34 | "homepage": "https://github.com/linkedin/opticss#readme", 35 | "engines": { 36 | "node": "6.* || 8.* || >= 10.*" 37 | }, 38 | "publishConfig": { 39 | "access": "public" 40 | }, 41 | "dependencies": { 42 | "@opticss/element-analysis": "^0.6.2", 43 | "@opticss/template-api": "^0.7.0", 44 | "@opticss/util": "^0.7.0", 45 | "@types/debug": "4.1.4", 46 | "concat-with-sourcemaps": "^1.0.4", 47 | "css-property-parser": "^1.0.5", 48 | "debug": "^4.1.1", 49 | "object.values": "^1.0.4", 50 | "postcss": "^7.0.14", 51 | "postcss-selector-parser": "^6.0.2", 52 | "source-map": "^0.7.3", 53 | "source-map-support": "^0.5.3", 54 | "specificity": "^0.4.1", 55 | "typescript-collections": "^1.2.5", 56 | "typescript-memoize": "^1.0.0-alpha.3" 57 | }, 58 | "devDependencies": { 59 | "@opticss/code-style": "^0.6.0", 60 | "@opticss/simple-template": "^0.6.4", 61 | "@types/chai": "^4.0.4", 62 | "@types/mocha": "^5.2.6", 63 | "@types/node": "^11.13.8", 64 | "@types/random-js": "^1.0.30", 65 | "chai": "^4.1.2", 66 | "css-size": "^4.0.1", 67 | "istanbul": "^0.4.5", 68 | "mocha": "^6.1.4", 69 | "mocha-typescript": "^1.1.9", 70 | "random-js": "^2.0.0", 71 | "remap-istanbul": "^0.13.0", 72 | "resolve-cascade": "^0.6.2", 73 | "tslint": "^5.10.0", 74 | "typedoc": "^0.15.0-0", 75 | "typescript": "~3.4.4" 76 | }, 77 | "volta": { 78 | "node": "10.15.3", 79 | "yarn": "1.15.2" 80 | }, 81 | "gitHead": "ef310cb1b10dbc90cae4f859da146863f99d940b" 82 | } 83 | -------------------------------------------------------------------------------- /packages/opticss/src/Actions/Action.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents an action that can be performed by the optimizer. 3 | * All optimizations must be done as an action. This provides 4 | * a central point for creating logs and reports of the work 5 | * that was done. It can also be used to implement backtracking. 6 | */ 7 | import { SourcePosition } from "@opticss/element-analysis"; 8 | import { Keys } from "@opticss/util"; 9 | import * as postcss from "postcss"; 10 | 11 | import { Optimizations } from "../optimizations"; 12 | 13 | export abstract class Action { 14 | optimization: string; 15 | abstract perform(): this; 16 | abstract logString(): string; 17 | abstract readonly sourcePosition: SourcePosition | undefined | null; 18 | 19 | constructor(optimization: Keys) { 20 | this.optimization = optimization; 21 | } 22 | 23 | positionString(sourcePosition = this.sourcePosition, includeFilename = true): string { 24 | let posStr = ""; 25 | if (sourcePosition && sourcePosition.line > 0) { 26 | if (includeFilename && sourcePosition.filename) { 27 | posStr += sourcePosition.filename + ":"; 28 | } 29 | posStr += sourcePosition.line; 30 | if (sourcePosition.column) { 31 | posStr += ":" + sourcePosition.column; 32 | } 33 | } 34 | return posStr; 35 | } 36 | 37 | annotateLogMessage(message: string, sourcePosition?: SourcePosition | null, indent = 0): string { 38 | if (sourcePosition === undefined) { 39 | sourcePosition = this.sourcePosition; 40 | } 41 | let annotated = ""; 42 | if (sourcePosition) { 43 | annotated += this.positionString(sourcePosition) + " "; 44 | } 45 | let indentation = ""; 46 | for (let i = 0; i < indent; i++) { 47 | indentation += " "; 48 | } 49 | annotated += `[${this.optimization}] ${indentation}${message}`; 50 | return annotated; 51 | } 52 | 53 | nodeSourcePosition(node: postcss.Node) { 54 | if (node.source && node.source.start) { 55 | return { 56 | filename: node.source.input.file, 57 | line: node.source.start.line, 58 | column: node.source.start.column, 59 | }; 60 | } else { 61 | return undefined; 62 | } 63 | } 64 | } 65 | 66 | export abstract class MultiAction extends Action{ 67 | logString(): string { 68 | return this.logStrings().join("\n"); 69 | } 70 | abstract logStrings(): Array; 71 | } 72 | 73 | export function isMultiAction(action: Action): action is MultiAction { 74 | return action instanceof MultiAction; 75 | } 76 | 77 | export function stripNL(str: string): string { 78 | return str.replace(/[\r\n\s]+/gm, " "); 79 | } 80 | -------------------------------------------------------------------------------- /packages/opticss/src/Actions/actions/AnnotateMergeConflict.ts: -------------------------------------------------------------------------------- 1 | import { Element, SourcePosition } from "@opticss/element-analysis"; 2 | import * as postcss from "postcss"; 3 | 4 | import { Optimizations } from "../../OpticssOptions"; 5 | import { ParsedSelector } from "../../parseSelector"; 6 | import { Action } from "../Action"; 7 | 8 | /** 9 | * Changes a Rule's selector string. 10 | */ 11 | export class AnnotateMergeConflict extends Action { 12 | mergedDecl: postcss.Declaration; 13 | mergedSel: ParsedSelector; 14 | unmergedDecl: postcss.Declaration; 15 | unmergedSel: ParsedSelector; 16 | conflictDecl: postcss.Declaration; 17 | conflictSel: ParsedSelector; 18 | element: Element; 19 | 20 | constructor( 21 | mergedDecl: postcss.Declaration, 22 | mergedSel: ParsedSelector, 23 | unmergedDecl: postcss.Declaration, 24 | unmergedSel: ParsedSelector, 25 | conflictDecl: postcss.Declaration, 26 | conflictSel: ParsedSelector, 27 | element: Element, 28 | optimization: keyof Optimizations = "mergeDeclarations", 29 | ) { 30 | super(optimization); 31 | this.mergedDecl = mergedDecl; 32 | this.mergedSel = mergedSel; 33 | this.unmergedDecl = unmergedDecl; 34 | this.unmergedSel = unmergedSel; 35 | this.conflictDecl = conflictDecl; 36 | this.conflictSel = conflictSel; 37 | this.element = element; 38 | } 39 | 40 | get sourcePosition(): SourcePosition | undefined { 41 | return this.nodeSourcePosition(this.conflictDecl); 42 | } 43 | perform(): this { 44 | return this; 45 | } 46 | logString(): string { 47 | let mainPosition = this.sourcePosition; 48 | let mergePos = this.nodeSourcePosition(this.mergedDecl); 49 | let omitMergeFile = mainPosition && mergePos && mainPosition.filename === mergePos.filename; 50 | let mergeStr = `${this.declString(this.mergedSel, this.mergedDecl)} (at ${this.positionString(mergePos, omitMergeFile)})`; 51 | let unmergedPos = this.nodeSourcePosition(this.unmergedDecl); 52 | let omitUnmergedFile = mainPosition && unmergedPos && mainPosition.filename === unmergedPos.filename; 53 | let unmergedStr = `${this.declString(this.unmergedSel, this.unmergedDecl)} (at ${this.positionString(unmergedPos, omitUnmergedFile)})`; 54 | let conflictStr = this.declString(this.conflictSel, this.conflictDecl); 55 | let elStr = this.elementString(); 56 | return this.annotateLogMessage( 57 | `Couldn't merge ${unmergedStr} with ${mergeStr} because it conflicts with ${conflictStr} on element ${elStr}`); 58 | } 59 | 60 | elementString(element: Element = this.element) { 61 | let elStr = element.toString(); 62 | let elPos = this.positionString(element.sourceLocation.start); 63 | if (elPos) { 64 | elStr += ` (at ${elPos})`; 65 | } 66 | return elStr; 67 | } 68 | 69 | declString(selector: ParsedSelector, decl: postcss.Declaration): string { 70 | return `${selector} { ${decl.prop}: ${decl.value}${decl.important ? " !important" : ""}; }`; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/opticss/src/Actions/actions/ChangeSelector.ts: -------------------------------------------------------------------------------- 1 | import { SourcePosition } from "@opticss/element-analysis"; 2 | import * as postcss from "postcss"; 3 | 4 | import { Optimizations } from "../../OpticssOptions"; 5 | import { SelectorCache } from "../../query"; 6 | import { Action, stripNL } from "../Action"; 7 | 8 | /** 9 | * Changes a Rule's selector string. 10 | */ 11 | export class ChangeSelector extends Action { 12 | reason: string; 13 | rule: postcss.Rule; 14 | oldSelector: string; 15 | newSelector: string; 16 | cache: SelectorCache; 17 | constructor(rule: postcss.Rule, newSelector: string, optimization: keyof Optimizations, reason: string, cache: SelectorCache) { 18 | super(optimization); 19 | this.reason = reason; 20 | this.rule = rule; 21 | this.oldSelector = rule.selector; 22 | this.newSelector = newSelector; 23 | this.cache = cache; 24 | } 25 | get sourcePosition(): SourcePosition | undefined { 26 | if (this.rule.source && this.rule.source.start) { 27 | return { 28 | filename: this.rule.source.input.file, 29 | line: this.rule.source.start.line, 30 | column: this.rule.source.start.column, 31 | }; 32 | } else { 33 | return undefined; 34 | } 35 | } 36 | perform(): this { 37 | this.cache.reset(this.rule); 38 | this.rule.selector = this.newSelector; 39 | return this; 40 | } 41 | logString(): string { 42 | return this.annotateLogMessage(`Changed selector from "${stripNL(this.oldSelector)}" to "${stripNL(this.newSelector)}" because ${this.reason}.`); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/opticss/src/Actions/actions/ExpandShorthand.ts: -------------------------------------------------------------------------------- 1 | import { SourcePosition } from "@opticss/element-analysis"; 2 | import * as postcss from "postcss"; 3 | 4 | import { Optimizations } from "../../OpticssOptions"; 5 | import { DeclarationInfo } from "../../optimizations/MergeDeclarations/StyleInfo"; 6 | import { Action } from "../Action"; 7 | 8 | /** 9 | * Merges duplicate declarations from multiple rule sets into a new rule set. 10 | */ 11 | export class ExpandShorthand extends Action { 12 | decl: postcss.Declaration; 13 | rule: postcss.Rule; 14 | decls: Array; 15 | newDecls: Array; 16 | reason: string; 17 | constructor( 18 | decl: postcss.Declaration, 19 | decls: Array, 20 | optimization: keyof Optimizations, 21 | reason: string, 22 | ) { 23 | super(optimization); 24 | this.reason = reason; 25 | this.decl = decl; 26 | this.rule = decl.parent; 27 | this.decls = decls; 28 | this.newDecls = []; 29 | } 30 | 31 | perform(): this { 32 | let expanded = new Set(); 33 | for (let decl of this.decls) { 34 | decl.expanded = true; 35 | if (expanded.has(decl.prop)) continue; 36 | expanded.add(decl.prop); 37 | let declNode = postcss.decl(decl); 38 | declNode.raws = { before: " ", after: " "}; 39 | this.rule.insertBefore(this.decl, declNode); 40 | let newDecl = this.decl.prev(); 41 | this.newDecls.push(newDecl); 42 | } 43 | return this; 44 | } 45 | 46 | logString(): string { 47 | return this.annotateLogMessage(`Expanded ${declString(this.decl)} into ${this.newDecls.map(d => declString(d)).join(" ")}. ${this.reason}`); 48 | } 49 | 50 | get sourcePosition(): SourcePosition | undefined { 51 | return this.nodeSourcePosition(this.decl); 52 | } 53 | } 54 | 55 | function declString(decl: {prop: string; value: string; important: boolean}): string { 56 | return `${decl.prop}: ${decl.value}${decl.important ? " !important" : ""};`; 57 | } 58 | -------------------------------------------------------------------------------- /packages/opticss/src/Actions/actions/MarkAttributeValueObsolete.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SourcePosition, 3 | } from "@opticss/element-analysis"; 4 | import { 5 | SimpleAttribute, 6 | simpleAttributeToString, 7 | } from "@opticss/template-api"; 8 | import { Keys } from "@opticss/util"; 9 | 10 | import { OptimizationPass } from "../../OptimizationPass"; 11 | import { Optimizations } from "../../optimizations"; 12 | import { ParsedSelectorAndRule } from "../../query"; 13 | import { MultiAction } from "../Action"; 14 | 15 | /** 16 | * Note that an attribute value is not used and will be removed from the 17 | * template. 18 | */ 19 | export class MarkAttributeValueObsolete extends MultiAction { 20 | reason: string; 21 | pass: OptimizationPass; 22 | selectors: ParsedSelectorAndRule[]; 23 | attribute: SimpleAttribute; 24 | 25 | constructor( 26 | pass: OptimizationPass, 27 | selectors: ParsedSelectorAndRule[], 28 | attribute: SimpleAttribute, 29 | reason: string, 30 | optimization: Keys = "mergeDeclarations", 31 | ) { 32 | super(optimization); 33 | this.pass = pass; 34 | this.selectors = selectors; 35 | this.attribute = attribute; 36 | this.reason = reason; 37 | } 38 | get sourcePosition(): SourcePosition | undefined { 39 | if (this.selectors.length > 0 && this.selectors[0].rule.source && this.selectors[0].rule.source.start) { 40 | return this.selectors[0].rule.source.start; 41 | } else { 42 | return undefined; 43 | } 44 | } 45 | perform(): this { 46 | this.pass.styleMapping.attributeIsObsolete(this.attribute); 47 | return this; 48 | } 49 | 50 | logStrings(): Array { 51 | let logs = new Array(); 52 | let mainMessage = `Attribute ${simpleAttributeToString(this.attribute)} will be removed from templates. ${this.reason}`; 53 | let firstPos: SourcePosition | undefined; 54 | if (this.selectors.length > 0) { 55 | firstPos = this.nodeSourcePosition(this.selectors[0].rule); 56 | if (firstPos) { firstPos.line = -1; } 57 | } 58 | logs.push(this.annotateLogMessage(mainMessage, firstPos)); 59 | for (let sel of this.selectors) { 60 | let rulePos = this.nodeSourcePosition(sel.rule); 61 | let msg = `Was used in selector: ${sel.parsedSelector}`; 62 | logs.push(this.annotateLogMessage(msg, rulePos, 1)); 63 | } 64 | return logs; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /packages/opticss/src/Actions/actions/Note.ts: -------------------------------------------------------------------------------- 1 | import { SourcePosition } from "@opticss/element-analysis"; 2 | 3 | import { Optimizations } from "../../OpticssOptions"; 4 | import { Action } from "../Action"; 5 | 6 | /** 7 | * Make a note about something discovered during optimization. 8 | */ 9 | export class Note extends Action { 10 | private _position: SourcePosition | undefined; 11 | reason: string; 12 | constructor(optimization: keyof Optimizations, reason: string, position?: SourcePosition) { 13 | super(optimization); 14 | this.reason = reason; 15 | this._position = position; 16 | } 17 | 18 | get sourcePosition(): SourcePosition | undefined { 19 | return this._position; 20 | } 21 | 22 | perform(): this { 23 | return this; 24 | } 25 | 26 | logString(): string { 27 | return this.annotateLogMessage(this.reason); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/opticss/src/Actions/actions/RemoveRule.ts: -------------------------------------------------------------------------------- 1 | import { SourcePosition } from "@opticss/element-analysis"; 2 | import * as postcss from "postcss"; 3 | 4 | import { Optimizations } from "../../OpticssOptions"; 5 | import { SelectorCache } from "../../query"; 6 | import { Action, stripNL } from "../Action"; 7 | 8 | /** 9 | * Removes a Rule. 10 | * Keeps track of the parent to which it belonged and the prev sibling. 11 | */ 12 | export class RemoveRule extends Action { 13 | reason: string; 14 | parent: postcss.Container; 15 | prevSibling: postcss.Node | undefined; 16 | rule: postcss.Rule; 17 | cache: SelectorCache; 18 | constructor(rule: postcss.Rule, optimization: keyof Optimizations, reason: string, cache: SelectorCache) { 19 | super(optimization); 20 | this.reason = reason; 21 | this.parent = rule.parent; 22 | this.prevSibling = rule.prev() || undefined; 23 | this.rule = rule; 24 | this.cache = cache; 25 | } 26 | perform(): this { 27 | this.cache.reset(this.rule); 28 | this.rule.remove(); 29 | return this; 30 | } 31 | get oldSelector() { 32 | return this.rule.selector; 33 | } 34 | logString(): string { 35 | return this.annotateLogMessage(`Removed rule with selector "${stripNL(this.oldSelector)}" because ${this.reason}.`); 36 | } 37 | get sourcePosition(): SourcePosition | undefined { 38 | if (this.rule.source && this.rule.source.start) { 39 | return { 40 | filename: this.rule.source.input.file, 41 | line: this.rule.source.start.line, 42 | column: this.rule.source.start.column, 43 | }; 44 | } else { 45 | return undefined; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/opticss/src/Actions/actions/Warning.ts: -------------------------------------------------------------------------------- 1 | import { Note } from "./Note"; 2 | 3 | export class Warning extends Note { 4 | } 5 | -------------------------------------------------------------------------------- /packages/opticss/src/Actions/index.ts: -------------------------------------------------------------------------------- 1 | import { Action } from "./Action"; 2 | 3 | /** 4 | * Tracks the actions that were performed and the order in which they were done. 5 | */ 6 | export class Actions { 7 | performed: Array; 8 | constructor() { 9 | this.performed = []; 10 | } 11 | perform(action: Action) { 12 | this.performed.push(action.perform()); 13 | } 14 | logStrings(): Array { 15 | return this.performed.map(a => a.logString()); 16 | } 17 | // TODO: Add undo method. 18 | } 19 | 20 | // All possible actions 21 | export { AnnotateMergeConflict } from "./actions/AnnotateMergeConflict"; 22 | export { ChangeSelector } from "./actions/ChangeSelector"; 23 | export { ExpandShorthand } from "./actions/ExpandShorthand"; 24 | export { MarkAttributeValueObsolete } from "./actions/MarkAttributeValueObsolete"; 25 | export { RemoveRule } from "./actions/RemoveRule"; 26 | export { RewriteRuleIdents } from "./actions/RewriteRuleIdents"; 27 | export { MergeDeclarations } from "./actions/MergeDeclarations"; 28 | export { Note } from "./actions/Note"; 29 | export { Warning } from "./actions/Warning"; 30 | export * from "./Action"; 31 | -------------------------------------------------------------------------------- /packages/opticss/src/CssFile.ts: -------------------------------------------------------------------------------- 1 | import * as postcss from "postcss"; 2 | import { RawSourceMap } from "source-map"; 3 | 4 | import { LegacyRawSourceMap, adaptFromLegacySourceMap } from "./util/adaptSourceMap"; 5 | 6 | /** 7 | * Represents a single CSS file and its associated meta-data. 8 | */ 9 | export interface CssFile { 10 | /** 11 | * A CSS file's contents. If this was previously processed with postcss, just 12 | * pass the parsed contents along -- no sense in making a bunch of extra work 13 | * for ourselves. 14 | */ 15 | content: postcss.Result | string; 16 | 17 | /** 18 | * The path to the file's contents. This is used for debugging purposes the 19 | * contents are not read from the file name's location. 20 | */ 21 | filename?: string; 22 | 23 | /** 24 | * If the file was processed, a sourcemap should be provided. 25 | * If a postcss.Result is returned for contents, the sourcemap from that 26 | * object will be used if this property is not set. 27 | */ 28 | sourceMap?: LegacyRawSourceMap | RawSourceMap | string; 29 | } 30 | 31 | export interface ParsedCssFile { 32 | content: postcss.Result; 33 | 34 | /** 35 | * The path to the file's contents. This is used for debugging purposes the 36 | * contents are not read from the file name's location. 37 | */ 38 | filename?: string; 39 | } 40 | 41 | /** 42 | * Given a CssFile, return the source map. 43 | * @param file CssFile 44 | * @returns The RawSourceMap or source map string, if present. 45 | */ 46 | export function sourceMapFromCssFile(file: CssFile): RawSourceMap | string | undefined { 47 | let sourceMap: LegacyRawSourceMap | RawSourceMap | string | undefined = file.sourceMap; 48 | if (!sourceMap && (file.content).map) { 49 | sourceMap = (file.content).map.toJSON(); 50 | } 51 | if (typeof sourceMap === "object") { 52 | return adaptFromLegacySourceMap(sourceMap); 53 | } else { 54 | return sourceMap; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/opticss/src/Match/Match.ts: -------------------------------------------------------------------------------- 1 | import { assertNever } from "@opticss/util"; 2 | 3 | export enum Match { 4 | /** 5 | * The element will definitively match the selector or selector component in 6 | * at least dynamic one state. 7 | */ 8 | yes = 1, 9 | /** 10 | * The element may match the selector or selector component; information is 11 | * ambiguous. 12 | */ 13 | maybe, 14 | /** The element will not match the selector or selector component. */ 15 | no, 16 | /** 17 | * The element is unrelated to the selector or selector component and no 18 | * information about whether the element matches can be determined. 19 | */ 20 | pass, 21 | } 22 | 23 | /** 24 | * true => Match.yes 25 | * false => Match.no 26 | * null => Match.pass 27 | * undefined => Match.maybe 28 | */ 29 | export function boolToMatch(value: boolean | null | undefined): Match { 30 | if (value === true) { 31 | return Match.yes; 32 | } else if (value === false) { 33 | return Match.no; 34 | } else if (value === undefined) { 35 | return Match.maybe; 36 | } else { 37 | return Match.pass; 38 | } 39 | } 40 | 41 | export function matchToBool(match: Match): boolean | null | undefined { 42 | switch (match) { 43 | case Match.yes: 44 | return true; 45 | case Match.no: 46 | return false; 47 | case Match.maybe: 48 | return undefined; 49 | case Match.pass: 50 | return null; 51 | default: 52 | return assertNever(match); 53 | } 54 | } 55 | 56 | export function matches(m: Match): boolean { 57 | return m === Match.yes || m === Match.maybe; 58 | } 59 | 60 | export function rejects(m: Match): boolean { 61 | return m === Match.no; 62 | } 63 | 64 | export function negate(m: Match): Match { 65 | if (matches(m)) { 66 | return Match.no; 67 | } else if (rejects(m)) { 68 | return Match.yes; 69 | } else { 70 | return m; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/opticss/src/Match/Matcher.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Selectable, 3 | } from "@opticss/element-analysis"; 4 | import * as SelectorParser from "postcss-selector-parser"; 5 | 6 | import { ParsedSelector } from "../parseSelector"; 7 | 8 | import { Match, matches } from "./Match"; 9 | 10 | export interface HasSelectorNodes { 11 | nodes: Array; 12 | } 13 | 14 | export abstract class Matcher { 15 | 16 | /** 17 | * @param [keySelectorOnly=true] When false, this selector can match against 18 | * any compound selector in the parsed selector. 19 | */ 20 | matchSelector(selectable: Type, parsedSelector: ParsedSelector, keySelectorOnly: boolean): Match { 21 | let matched = parsedSelector.eachCompoundSelector((selector) => { 22 | if (selector !== parsedSelector.key && keySelectorOnly) return; 23 | let match = this.matchSelectorComponent(selectable, selector); 24 | if (matches(match)) return match; 25 | return; 26 | }); 27 | return matched || Match.no; 28 | } 29 | 30 | // Abstract methods for subclasses to implement 31 | abstract matchSelectorComponent(selectable: Selectable, selector: HasSelectorNodes): Match; 32 | abstract matchSelectorNode(selectable: Selectable, selector: SelectorParser.Node): Match; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /packages/opticss/src/Match/TagMatcher.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Tag, 3 | isConstant, 4 | isTagChoice, 5 | isUnknown, 6 | } from "@opticss/element-analysis"; 7 | import { assertNever } from "@opticss/util"; 8 | import * as SelectorParser from "postcss-selector-parser"; 9 | 10 | import { Match, boolToMatch } from "./Match"; 11 | import { HasSelectorNodes, Matcher } from "./Matcher"; 12 | 13 | export function isTag(tag: { type: string } | undefined): tag is SelectorParser.Tag { 14 | if (tag) { 15 | return tag.type === SelectorParser.TAG; 16 | } else { 17 | return false; 18 | } 19 | } 20 | 21 | export class TagMatcher extends Matcher { 22 | private constructor() { super(); } 23 | private static _instance = new TagMatcher(); 24 | public static get instance() { 25 | return TagMatcher._instance; 26 | } 27 | matchSelectorNode(tag: Tag, node: SelectorParser.Node): Match { 28 | if (isTag(node)) { 29 | if (isConstant(tag.value)) { 30 | return boolToMatch(node.value === tag.value.constant); 31 | } else if (isTagChoice(tag.value)) { 32 | return boolToMatch(tag.value.oneOf.some(v => v === node.value)); 33 | } else if (isUnknown(tag.value)) { 34 | return Match.maybe; 35 | } else { 36 | return assertNever(node); 37 | } 38 | } else { 39 | return Match.pass; 40 | } 41 | } 42 | 43 | matchSelectorComponent(tag: Tag, selector: HasSelectorNodes): Match { 44 | let tagNode = this.getTag(selector); 45 | if (tagNode) { 46 | return this.matchSelectorNode(tag, tagNode); 47 | } else { 48 | return Match.pass; 49 | } 50 | } 51 | 52 | private getTag(selector: HasSelectorNodes): SelectorParser.Tag | undefined { 53 | let node = selector.nodes.find((node) => isTag(node)); 54 | if (node) { 55 | return node as SelectorParser.Tag; 56 | } else { 57 | return undefined; 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /packages/opticss/src/Match/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Provides a matching utility to resolve selectors against elements discovered 3 | * during template analysis. 4 | * 5 | * Provided a `Selectable` from '@opticss/template-api', and a `ParsedSelector` from 6 | * 'postcss-selector-parser', determine if the `ParsedSelector` matches the Selectable. 7 | * 8 | * Matches are of type `enum Match`, defined in './Match, and may be of type `Match.yes`, 9 | * `Match.no`, `Match.maybe`, `Match.pass`. 10 | */ 11 | export * from "./Match"; 12 | export { AttributeMatcher } from "./AttributeMatcher"; 13 | export { TagMatcher } from "./TagMatcher"; 14 | export { ElementMatcher } from "./ElementMatcher"; 15 | -------------------------------------------------------------------------------- /packages/opticss/src/OpticssOptions.ts: -------------------------------------------------------------------------------- 1 | import { RewritableIdents } from "@opticss/template-api"; 2 | 3 | export interface Optimizations { 4 | /** 5 | * Whether to rewrite idents where possible. 6 | * Note: ident rewriting will only be enabled for the types of idents 7 | * that the rewriter can support. 8 | */ 9 | rewriteIdents: boolean | RewritableIdents; 10 | 11 | /** 12 | * Whether to remove styles that are not in use according to the template 13 | * analysis. 14 | */ 15 | removeUnusedStyles: boolean; 16 | 17 | /** 18 | * Whether to merge declarations across compatible selectors. 19 | */ 20 | mergeDeclarations: boolean; 21 | } 22 | 23 | export interface OptiCSSOptions extends Optimizations { 24 | /** 25 | * Whether to perform any optimizations. 26 | */ 27 | enabled: boolean; 28 | /** 29 | * Only perform the optimizations specified and no others. 30 | */ 31 | only?: Array; 32 | /** 33 | * Perform all optimizations except the ones specified. Overrides 34 | * optimizations enabled by the `only` option. 35 | */ 36 | except?: Array; 37 | 38 | /** 39 | * Some CSS features can be used for more optimal output but may have 40 | * varying level of support. These options control wether the optimizer 41 | * will take advantage of those features where it can. 42 | * 43 | * CSS features are never output with vendor prefixes. You can try using 44 | * autoprefixer or cssnext, but doing so is likely to result in output that 45 | * is less optimal than if the optimization hadn't been performed. 46 | */ 47 | css?: Partial; 48 | 49 | identifiers?: { 50 | /** 51 | * Sets the starting value for identifiers. This is a standard base-10 number 52 | * that is converted to a corresponding identifier. 53 | * 54 | * An integer greater than or equal to 1. 55 | * Defaults to 1. 56 | */ 57 | startValue?: number; 58 | 59 | /** 60 | * How many identifiers the ident generator for each namespace should be 61 | * allowed to produce. Note that if any of the produced identifiers are 62 | * reserved, the actual number of identifiers returned will be less than 63 | * the max. 64 | * 65 | * An integer greater than or equal to 1. 66 | * Defaults to Infinity. 67 | */ 68 | maxCount?: number; 69 | }; 70 | } 71 | 72 | export const DEFAULT_OPTIONS = Object.freeze({ 73 | enabled: true, 74 | rewriteIdents: true, 75 | removeUnusedStyles: true, 76 | mergeDeclarations: true, 77 | css: {}, 78 | }); 79 | 80 | export interface CSSFeatureFlags { 81 | useMatchesPseudoClass: boolean; 82 | 83 | /** 84 | * Indicates that class and id selectors should be treated as case-insensitive. 85 | * In quirksmode and some older doctypes, selectors are case insensitive. 86 | * 87 | * Identifiers are more compressible when case sensitivity can be assumed. 88 | */ 89 | caseInsensitiveSelectors: boolean; 90 | } 91 | -------------------------------------------------------------------------------- /packages/opticss/src/OptimizationPass.ts: -------------------------------------------------------------------------------- 1 | import { StyleMapping, TemplateIntegrationOptions } from "@opticss/template-api"; 2 | 3 | import { Actions } from "./Actions"; 4 | import { OptiCSSOptions } from "./OpticssOptions"; 5 | import { SelectorCache } from "./query"; 6 | import { IdentGenerators } from "./util/IdentGenerator"; 7 | 8 | export class OptimizationPass { 9 | styleMapping: StyleMapping; 10 | cache: SelectorCache; 11 | actions: Actions; 12 | identGenerators: IdentGenerators<"id" | "class">; 13 | constructor(options: OptiCSSOptions, templateOptions: TemplateIntegrationOptions) { 14 | this.styleMapping = new StyleMapping(templateOptions); 15 | this.cache = new SelectorCache(); 16 | this.actions = new Actions(); 17 | this.identGenerators = new IdentGenerators({ 18 | caseInsensitive: options.css!.caseInsensitiveSelectors!, 19 | namespaces: ["id", "class"], 20 | startValue: options.identifiers && options.identifiers.startValue, 21 | maxIdentCount: options.identifiers && options.identifiers.maxCount, 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/opticss/src/errors.ts: -------------------------------------------------------------------------------- 1 | export interface ErrorLocation { 2 | filename?: string; 3 | line?: number; 4 | column?: number; 5 | } 6 | 7 | type PrefixedConstructor = Function & { prefix?: string }; 8 | 9 | /** 10 | * Custom Opticss error base class. Will format `ErrorLocation` into thrown 11 | * error message if provided. 12 | */ 13 | export class OpticssError extends Error { 14 | static prefix = "Error"; 15 | origMessage: string; 16 | private _location?: ErrorLocation; 17 | constructor(message: string, location?: ErrorLocation) { 18 | super(message); 19 | this.origMessage = message; 20 | this._location = location; 21 | super.message = this.annotatedMessage(); 22 | } 23 | 24 | private annotatedMessage() { 25 | let loc = this.location; 26 | if (!loc) { 27 | return this.origMessage; 28 | } 29 | let filename = loc.filename || ""; 30 | let line = loc.line ? `:${loc.line}` : ""; 31 | let column = loc.column ? `:${loc.column}` : ""; 32 | let locMessage = ` (${filename}${line}${column})`; 33 | let constructor: PrefixedConstructor = this.constructor; 34 | let prefix = constructor.prefix || OpticssError.prefix; 35 | return `Opticss ${prefix}: ${this.origMessage}${locMessage}`; 36 | } 37 | 38 | get location(): ErrorLocation | undefined { 39 | return this._location; 40 | } 41 | 42 | } 43 | 44 | /** 45 | * Custom Opticss error type for template analysis errors. 46 | */ 47 | export class TemplateError extends OpticssError { 48 | static prefix = "TemplateError"; 49 | constructor(message: string, location?: ErrorLocation) { 50 | super(message, location); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/opticss/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as postcss from "postcss"; 2 | import * as postcssSelectorParser from "postcss-selector-parser"; 3 | import * as TSCollections from "typescript-collections"; 4 | 5 | export * from "./CssFile"; 6 | export * from "./errors"; 7 | export * from "./query"; 8 | export * from "./OpticssOptions"; 9 | export * from "./parseSelector"; 10 | export * from "./util/adaptSourceMap"; 11 | export * from "./util/IdentGenerator"; 12 | export * from "./Actions"; 13 | export { 14 | OptimizationResult, 15 | TimingData, 16 | Optimizer, 17 | } from "./Optimizer"; 18 | export { 19 | postcss, 20 | postcssSelectorParser, 21 | TSCollections, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/opticss/src/initializers/index.ts: -------------------------------------------------------------------------------- 1 | import { TemplateAnalysis, TemplateIntegrationOptions, TemplateTypes } from "@opticss/template-api"; 2 | 3 | import { ParsedCssFile } from "../CssFile"; 4 | import { OptiCSSOptions } from "../OpticssOptions"; 5 | import { OptimizationPass } from "../OptimizationPass"; 6 | 7 | import { initKnownIdents } from "./initKnownIdents"; 8 | 9 | // The expected interface of an initializer function. 10 | export type Initializer = ( 11 | pass: OptimizationPass, 12 | analyses: Array>, 13 | files: Array, 14 | options: OptiCSSOptions, 15 | templateOptions: TemplateIntegrationOptions, 16 | ) => void; 17 | 18 | // Initializer Manifest Interface 19 | export interface Initializers { 20 | initKnownIdents: Initializer; 21 | } 22 | 23 | // Initializer Manifest 24 | export const initializers: Initializers = { 25 | initKnownIdents, 26 | }; 27 | -------------------------------------------------------------------------------- /packages/opticss/src/initializers/initKnownIdents.ts: -------------------------------------------------------------------------------- 1 | import { TemplateAnalysis, TemplateIntegrationOptions, TemplateTypes, rewriteOptions } from "@opticss/template-api"; 2 | import { assertNever } from "@opticss/util"; 3 | import * as SelectorParser from "postcss-selector-parser"; 4 | 5 | import { ParsedCssFile } from "../CssFile"; 6 | import { OptiCSSOptions } from "../OpticssOptions"; 7 | import { OptimizationPass } from "../OptimizationPass"; 8 | import { eachFileIdent } from "../util/cssIntrospection"; 9 | 10 | const { isClassName, isIdentifier } = SelectorParser; 11 | /** 12 | * Initializes this OptimizationPass' ident generator with blacklisted identifiers. 13 | * @param pass The OptimizationPass. 14 | * @param analyses - All analysis objects associated with this Optimization. 15 | * @param files - All parsed css files being optimized. 16 | * @param options - This Optimization's options. 17 | * @param templateOptions - The compatible options for this integratin's Template rewriter. 18 | */ 19 | export function initKnownIdents( 20 | pass: OptimizationPass, 21 | analyses: TemplateAnalysis[], 22 | files: ParsedCssFile[], 23 | options: OptiCSSOptions, 24 | templateOptions: TemplateIntegrationOptions, 25 | 26 | ): void { 27 | // Fetch normalized options 28 | let opts = rewriteOptions(options.rewriteIdents, templateOptions.rewriteIdents); 29 | 30 | // Reserve all idents specifically requested by the user. 31 | pass.identGenerators.reserve("class", ...opts.omitIdents.class); 32 | pass.identGenerators.reserve("id", ...opts.omitIdents.id); 33 | 34 | // Reserve every existing identifier used in any of the files 35 | eachFileIdent(files, pass.cache, opts, (node) => { 36 | if (isClassName(node)) { 37 | pass.identGenerators.reserve("class", node.value); 38 | pass.styleMapping.addSourceAttribute({name: "class", value: node.value}); 39 | } else if (isIdentifier(node)) { 40 | pass.identGenerators.reserve("id", node.value); 41 | pass.styleMapping.addSourceAttribute({name: "id", value: node.value}); 42 | } else { 43 | assertNever(node); 44 | } 45 | }); 46 | 47 | // Reserve all idents discovered in the Templates. 48 | for (let analysis of analyses) { 49 | let allConstants = analysis.constants(new Set(["class", "id"])); 50 | for (let constantType of Object.keys(allConstants)) { 51 | for (let constant of allConstants[constantType]) { 52 | pass.identGenerators.reserve(<"class" | "id">constantType, constant); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/opticss/src/optimizations/MergeDeclarations/StyleInfo.ts: -------------------------------------------------------------------------------- 1 | import { Element } from "@opticss/element-analysis"; 2 | import * as postcss from "postcss"; 3 | import * as specificity from "specificity"; 4 | import { MultiDictionary } from "typescript-collections"; 5 | 6 | import { ParsedCssFile } from "../../CssFile"; 7 | import { ParsedSelector } from "../../parseSelector"; 8 | import { RuleScope } from "../../util/cssIntrospection"; 9 | 10 | export interface SelectorInfo { 11 | /** The original rule node for eventual manipulation */ 12 | rule: postcss.Rule; 13 | /** The AtRules that scope this selector. */ 14 | scope: RuleScope; 15 | /** The selector parsed into compound selectors */ 16 | selector: ParsedSelector; 17 | container: postcss.Container; 18 | /** The specificity of this selector */ 19 | specificity: specificity.Specificity; 20 | /** The file this selector came from */ 21 | file: ParsedCssFile; 22 | /** 23 | * The overall index of this selector. 24 | * Selectors from files with bigger numbers override 25 | * selectors from files with smaller numbers. */ 26 | fileIndex: number; 27 | /** 28 | * A number indicating the general source order. higher numbers come later 29 | * in the source file. 30 | */ 31 | sourceIndex: number; 32 | /** The analyzed elements that this selector might match. */ 33 | elements: Array; 34 | /** 35 | * Quick compare for two selectors to see which wins. This is set once 36 | * all selectors are sorted initially. 37 | */ 38 | ordinal: number; 39 | /** 40 | * declarations this selector sets. 41 | * maps property name to [value, important] pairs. Multiple values are set 42 | * when the rule set assigns the same property multiple times as is often done 43 | * for progressive enhancement. 44 | */ 45 | declarations: MultiDictionary; 46 | 47 | /** 48 | * Maps property/value pairs that might be expanded to the declaration infos 49 | * for the declaration. 50 | */ 51 | declarationInfos: MultiDictionary<[string, string], DeclarationInfo>; 52 | } 53 | 54 | export interface DeclarationInfo { 55 | selectorInfo: SelectorInfo; 56 | decl: postcss.Declaration; 57 | prop: string; 58 | value: string; 59 | important: boolean; 60 | /** 61 | * A single number that can be compared with another DeclarationInfo 62 | * to understand which one came first in the document source order. 63 | * Smaller numbers are closer to the start of the file. 64 | */ 65 | sourceOrdinal: number; 66 | /** the original source ordinal value before the declaration was moved. */ 67 | originalSourceOrdinal: number; 68 | /** 69 | * A single number that can be compared with another DeclarationInfo 70 | * to understand which one wins if both are applied on the same element. 71 | * Bigger numbers win. 72 | */ 73 | ordinal: number; 74 | /** the original ordinal value before the declaration was moved. */ 75 | originalOrdinal: number; 76 | /** How many declarations this one can be merged with. */ 77 | dupeCount: number; 78 | /** whether this decl has been expanded yet. */ 79 | expanded: boolean; 80 | } 81 | -------------------------------------------------------------------------------- /packages/opticss/src/optimizations/Optimization.ts: -------------------------------------------------------------------------------- 1 | import { TemplateAnalysis, TemplateIntegrationOptions, TemplateTypes } from "@opticss/template-api"; 2 | 3 | import { ParsedCssFile } from "../CssFile"; 4 | import { Initializers } from "../initializers"; 5 | import { OptiCSSOptions } from "../OpticssOptions"; 6 | import { OptimizationPass } from "../OptimizationPass"; 7 | 8 | // Optimizations TODO: 9 | // ✓ Remove unused styles. 10 | // ✓ Rewrite idents (consider moving to end of optimization pipeline.) 11 | // * Reduce to order-of-a-class specificity (ID => class) 12 | // * Remove static selector scope (map class in scope to new class) 13 | // * Remove dynamic selector scope (map class in scope to new class when ancestor changes) 14 | // * Normalize values 15 | // * Convert initial to initial value 16 | // ✓ Merge declarations 17 | // * Combine selectors that are always applied to the same elements and merge their declarations into a single ruleset. 18 | // * Combine long-hands into shorthands (sometimes add a de-opt for merged prop) Open question: is this actually worse for browser perf? 19 | // * Combine rulesets with identical declarations to a single ruleset with multiple selectors (if doesn't introduce cascade issues) 20 | // * Remove redundant declarations where it's clear the properties aren't being progressively enhanced. 21 | 22 | export interface OptimizationConstructor { 23 | new (options: OptiCSSOptions, 24 | templateOptions: TemplateIntegrationOptions): Optimization; 25 | } 26 | 27 | export interface SingleFileOptimization { 28 | name: string; 29 | readonly initializers: Array; 30 | optimizeSingleFile( 31 | pass: OptimizationPass, 32 | analyses: Array>, 33 | file: ParsedCssFile): void; 34 | } 35 | 36 | export interface MultiFileOptimization { 37 | name: string; 38 | readonly initializers: Array; 39 | optimizeAllFiles( 40 | pass: OptimizationPass, 41 | analyses: Array>, 42 | files: Array): void; 43 | } 44 | 45 | export function isMultiFileOptimization(optimization: Optimization): optimization is MultiFileOptimization { 46 | return !!(optimization).optimizeAllFiles; 47 | } 48 | 49 | export function isSingleFileOptimization(optimization: Optimization): optimization is SingleFileOptimization { 50 | return !!(optimization).optimizeSingleFile; 51 | } 52 | 53 | export type Optimization = SingleFileOptimization 54 | | MultiFileOptimization 55 | | SingleFileOptimization & MultiFileOptimization; 56 | -------------------------------------------------------------------------------- /packages/opticss/src/optimizations/RewriteIdents.ts: -------------------------------------------------------------------------------- 1 | import { NormalizedRewriteOptions, TemplateAnalysis, TemplateIntegrationOptions, TemplateTypes, rewriteOptions } from "@opticss/template-api"; 2 | 3 | import { RewriteRuleIdents, RuleIdents } from "../Actions/actions/RewriteRuleIdents"; 4 | import { ParsedCssFile } from "../CssFile"; 5 | import { Initializers } from "../initializers"; 6 | import { OptiCSSOptions } from "../OpticssOptions"; 7 | import { OptimizationPass } from "../OptimizationPass"; 8 | import { eachFileIdent } from "../util/cssIntrospection"; 9 | 10 | import { MultiFileOptimization } from "./Optimization"; 11 | 12 | export class RewriteIdents implements MultiFileOptimization { 13 | name = "rewriteIdents"; 14 | initializers: Array = ["initKnownIdents"]; 15 | 16 | rewriteOptions: NormalizedRewriteOptions; 17 | constructor(options: OptiCSSOptions, templateOptions: TemplateIntegrationOptions) { 18 | this.rewriteOptions = rewriteOptions(options.rewriteIdents, templateOptions.rewriteIdents); 19 | } 20 | optimizeAllFiles( 21 | pass: OptimizationPass, 22 | _analyses: Array>, 23 | files: ParsedCssFile[], 24 | ): void 25 | { 26 | let allIdents = new Array(); 27 | let currentIdents: RuleIdents | undefined = undefined; 28 | eachFileIdent(files, pass.cache, this.rewriteOptions, (node, rule, selector) => { 29 | if (currentIdents && (currentIdents.rule !== rule) || !currentIdents) { 30 | if (currentIdents) { 31 | allIdents.push(currentIdents); 32 | } 33 | currentIdents = { 34 | rule, 35 | selectors: [selector], 36 | idents: [node], 37 | }; 38 | } else { 39 | let lastSelector = 40 | currentIdents.selectors[currentIdents.selectors.length - 1]; 41 | if (lastSelector !== selector) { 42 | currentIdents.selectors.push(selector); 43 | } 44 | currentIdents.idents.push(node); 45 | } 46 | }); 47 | if (currentIdents) { 48 | allIdents.push(currentIdents); 49 | } 50 | 51 | allIdents.forEach(ident => { 52 | pass.actions.perform(new RewriteRuleIdents(pass, ident)); 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/opticss/src/optimizations/index.ts: -------------------------------------------------------------------------------- 1 | import { MergeDeclarations } from "./MergeDeclarations"; 2 | import { OptimizationConstructor } from "./Optimization"; 3 | import { RemoveUnusedStyles } from "./RemoveUnusedStyles"; 4 | import { RewriteIdents } from "./RewriteIdents"; 5 | 6 | export { 7 | Optimization, 8 | OptimizationConstructor, 9 | MultiFileOptimization, 10 | SingleFileOptimization, 11 | isMultiFileOptimization, 12 | isSingleFileOptimization, 13 | } from "./Optimization"; 14 | 15 | export interface Optimizations { 16 | removeUnusedStyles: typeof RemoveUnusedStyles; 17 | rewriteIdents: typeof RewriteIdents; 18 | mergeDeclarations: typeof MergeDeclarations; 19 | [optimization: string]: OptimizationConstructor; 20 | } 21 | 22 | // TODO: enforce execution order of optimizations listed here. 23 | export const optimizations: Optimizations = { 24 | removeUnusedStyles: RemoveUnusedStyles, 25 | mergeDeclarations: MergeDeclarations, 26 | rewriteIdents: RewriteIdents, 27 | }; 28 | -------------------------------------------------------------------------------- /packages/opticss/src/util/adaptSourceMap.ts: -------------------------------------------------------------------------------- 1 | import { RawSourceMap } from "source-map"; 2 | 3 | // This is the old version of RawSourceMap 4 | export interface LegacyRawSourceMap { 5 | file?: string; 6 | sourceRoot?: string; 7 | version: string; 8 | sources: string[]; 9 | names: string[]; 10 | sourcesContent?: string[]; 11 | mappings: string; 12 | } 13 | 14 | /** 15 | * The type of RawSourceMap changed in a backwards incompatible way. 16 | * This function adapts code that might return an old version of the 17 | * RawSourceMap to match the current definition. 18 | */ 19 | export function adaptFromLegacySourceMap(sourceMap: LegacyRawSourceMap | RawSourceMap): RawSourceMap { 20 | // The legacy version was a number even though the type said it was a string. 21 | // But we handle strings here juuuust in case. 22 | let version = 23 | typeof sourceMap.version === "string" 24 | ? parseInt(sourceMap.version) 25 | : sourceMap.version; 26 | let newMap = { 27 | file: sourceMap.file || "", 28 | sourceRoot: sourceMap.sourceRoot, 29 | version, 30 | sources: sourceMap.sources, 31 | names: sourceMap.names, 32 | sourcesContent: sourceMap.sourcesContent, 33 | mappings: sourceMap.mappings, 34 | }; 35 | return newMap; 36 | } 37 | 38 | /** 39 | * The type of RawSourceMap changed in a backwards incompatible way. 40 | * This function adapts code that might require an old version of the 41 | * RawSourceMap. 42 | */ 43 | export function adaptToLegacySourceMap(sourceMap: LegacyRawSourceMap | RawSourceMap): LegacyRawSourceMap { 44 | let newMap = { 45 | file: sourceMap.file || "", 46 | sourceRoot: sourceMap.sourceRoot, 47 | // The version was never actually a string, we cast through any to match the legacy behavior. 48 | // tslint:disable-next-line:prefer-unknown-to-any 49 | version: sourceMap.version as any, 50 | sources: sourceMap.sources, 51 | names: sourceMap.names, 52 | sourcesContent: sourceMap.sourcesContent, 53 | mappings: sourceMap.mappings, 54 | }; 55 | return newMap; 56 | } 57 | -------------------------------------------------------------------------------- /packages/opticss/src/util/shorthandProperties.ts: -------------------------------------------------------------------------------- 1 | import { SourcePosition } from "@opticss/element-analysis"; 2 | import { StringDict } from "@opticss/util"; 3 | import * as propParser from "css-property-parser"; 4 | import * as postcss from "postcss"; 5 | 6 | import { 7 | Actions, 8 | Warning, 9 | } from "../Actions"; 10 | 11 | export function expandPropertyName(prop: string, recursively = false): string[] { 12 | let props = propParser.getShorthandComputedProperties(prop, recursively); 13 | if (recursively) { 14 | return props.filter(p => propParser.isShorthandProperty(p)); 15 | } else { 16 | return props; 17 | } 18 | } 19 | 20 | export function expandIfNecessary(authoredProps: Set, prop: string, value: string, actions: Actions, decl: postcss.Declaration): StringDict { 21 | if (!propParser.isShorthandProperty(prop)) { 22 | return {[prop]: value}; 23 | } 24 | let longhandDeclarations: StringDict = {}; 25 | let longHandProps; 26 | let longHandValues; 27 | try { 28 | longHandValues = propParser.expandShorthandProperty(prop, value, false, true); 29 | longHandProps = Object.keys(longHandValues); 30 | } catch (e) { 31 | if (/parsing shorthand property/.test(e.message)) { 32 | actions.perform(new Warning("mergeDeclarations", e.message + ` (long hands for this declaration will not be optimized)`, sourcePositionForNode(decl))); 33 | return { [prop]: value }; 34 | } else if (/is not a supported property/.test(e.message)) { 35 | actions.perform(new Warning("mergeDeclarations", e.message + ` (long hands for this declaration with conflicting values will not be understood as such which could result in incorrect optimization output.)`, sourcePositionForNode(decl))); 36 | return { [prop]: value }; 37 | } else { 38 | throw e; 39 | } 40 | } 41 | let directAuthored = longHandProps.some(p => authoredProps.has(p)); 42 | for (let p of longHandProps) { 43 | let v = longHandValues[p]; 44 | let expanded = expandIfNecessary(authoredProps, p, v, actions, decl); 45 | if (Object.keys(expanded).some(key => authoredProps.has(key))) { 46 | Object.assign(longhandDeclarations, expanded); 47 | } else if (directAuthored) { 48 | Object.assign(longhandDeclarations, {[p]: v}); 49 | } 50 | } 51 | if (Object.keys(longhandDeclarations).length === 0) { 52 | longhandDeclarations[prop] = value; 53 | } 54 | return longhandDeclarations; 55 | } 56 | 57 | function sourcePositionForNode(node: postcss.NodeBase): SourcePosition | undefined { 58 | if (node.source && node.source.start) { 59 | return { 60 | filename: node.source.input.file, 61 | line: node.source.start.line, 62 | column: node.source.start.column, 63 | }; 64 | } else { 65 | return undefined; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /packages/opticss/test/fixtures/integration-tests/simple/markup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | A Simple Test 8 | 9 | 10 |

A Simple Test

11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
14 | Column 1Column 2Column 3Column 4
OneTwoThreeFour
27 |
28 |
A Label 1: value
29 |
A Label 2: value
30 |
31 |
32 |
B Label 1: value
33 |
B Label 2: value
34 |
35 |
36 |
C Label 1: value
37 |
C Label 2: value
38 |
39 |

Lorem, ipsum dolor sit amet consectetur adipisicing elit. Nesciunt, quaerat incidunt consequuntur ex delectus nisi exercitationem similique culpa totam ea libero ut doloremque soluta repudiandae aliquid eaque expedita a praesentium!

40 |

Lorem, ipsum dolor sit amet consectetur adipisicing elit. Nesciunt, quaerat incidunt consequuntur ex delectus nisi exercitationem similique culpa totam ea libero ut doloremque soluta repudiandae aliquid eaque expedita a praesentium!

41 | 42 | -------------------------------------------------------------------------------- /packages/opticss/test/fixtures/integration-tests/simple/styles.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | 50 | .heading { 51 | 52 | } 53 | 54 | .sans { 55 | font-family: "Lato", sans-serif; 56 | } 57 | 58 | .serif { 59 | font-family: "Enriqueta", sans-serif; 60 | } 61 | 62 | .bold { 63 | font-weight: 800; 64 | } 65 | 66 | .italic { 67 | font-style: italic; 68 | } 69 | 70 | .underline { 71 | text-decoration: underline; 72 | } 73 | 74 | .xsmall { 75 | font-size: 1.0rem; 76 | } 77 | 78 | .small { 79 | font-size: 1.2rem; 80 | } 81 | 82 | .medium { 83 | font-size: 1.8rem; 84 | } 85 | 86 | .large { 87 | font-size: 2.4rem; 88 | } 89 | 90 | .xlarge { 91 | font-size: 3.8rem; 92 | font-weight: bold; 93 | } 94 | 95 | .foo-1, .foo-2, .foo-3 { 96 | display: block; 97 | clear: left; 98 | } 99 | 100 | .bar { 101 | display: block; 102 | float: left; 103 | } 104 | 105 | .label { 106 | font-weight: bold; 107 | } 108 | 109 | .layout-container { 110 | display: grid; 111 | grid-template: "a b"; 112 | } 113 | 114 | .layout-child-a { 115 | grid-area: a; 116 | } 117 | 118 | .layout-child-b { 119 | grid-area: b; 120 | } 121 | 122 | -------------------------------------------------------------------------------- /packages/opticss/test/integration-tests.ts: -------------------------------------------------------------------------------- 1 | import { TestTemplate } from "@opticss/simple-template"; 2 | import { assert } from "chai"; 3 | import * as fs from "fs"; 4 | import { 5 | slow, 6 | suite, 7 | test, 8 | timeout, 9 | } from "mocha-typescript"; 10 | import * as path from "path"; 11 | 12 | import { Warning } from "../src"; 13 | 14 | import { 15 | CascadeTestError, 16 | CascadeTestResult, 17 | debugCascadeError, 18 | debugError, 19 | logOptimizations, 20 | testOptimizationCascade, 21 | } from "./util/assertCascade"; 22 | 23 | // import { debugSize } from "./util/assertSmaller"; 24 | 25 | function testDefaults(...stylesAndTemplates: Array): Promise { 26 | return testOptimizationCascade( 27 | { }, 28 | { 29 | rewriteIdents: { id: true, class: true }, 30 | analyzedAttributes: [], 31 | analyzedTagnames: true, 32 | }, 33 | ...stylesAndTemplates).catch((e: CascadeTestError) => { 34 | debugError(stylesAndTemplates.filter(s => typeof s === "string").join("\n"), e); 35 | throw e; 36 | }); 37 | } 38 | 39 | @suite("Integration Tests", slow(3000), timeout(5000)) 40 | export class IntegrationTests { 41 | @test "simple page"() { 42 | let css = fs.readFileSync(path.resolve(__dirname, "../../test/fixtures/integration-tests/simple/styles.css"), "utf-8"); 43 | let markup = fs.readFileSync(path.resolve(__dirname, "../../test/fixtures/integration-tests/simple/markup.html"), "utf-8"); 44 | let template = new TestTemplate("test", markup, true); 45 | return testDefaults(css, template).then(result => { 46 | let warnings = result.optimization.actions.performed.filter(a => a instanceof Warning); 47 | assert.equal(warnings.length, 3); 48 | assert.equal(warnings[0].logString(), `${process.cwd()}/test1.css:111:3 [mergeDeclarations] ` + 49 | `Unsupported property error: grid-template is not a supported property ` + 50 | `(long hands for this declaration with conflicting values will not be understood as such which could result in incorrect optimization output.)`); 51 | // logOptimizations(result.optimization); 52 | // return debugSize(result).then(() => { 53 | // // debugResult(css, result); 54 | // }); 55 | }).catch(e => { 56 | if (e.optimization) { 57 | logOptimizations(e.optimization); 58 | } 59 | debugCascadeError(e); 60 | throw e; 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/opticss/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --require source-map-support/register 3 | --inline-diffs 4 | --recursive 5 | 6 | -------------------------------------------------------------------------------- /packages/opticss/test/opticss-test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | } from "chai"; 4 | import { 5 | suite, 6 | test, 7 | } from "mocha-typescript"; 8 | 9 | import { 10 | Optimizer, 11 | } from "../src/Optimizer"; 12 | 13 | @suite("OptiCSS") 14 | export class OptiCSSTest { 15 | @test "can be constructed and get a non-optimized output"() { 16 | let css1 = `.a { color: red; }`; 17 | let css2 = `.b { width: 100%; }`; 18 | let optimizer = new Optimizer( 19 | { enabled: false }, 20 | { rewriteIdents: { id: false, class: true }}, 21 | ); 22 | optimizer.addSource({content: css1, filename: "test1.css"}); 23 | optimizer.addSource({content: css2, filename: "test2.css"}); 24 | return optimizer.optimize("optimized.css").then(result => { 25 | let optimized = result.output.content.toString(); 26 | assert.equal(optimized, `${css1}\n${css2}`); 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/opticss/test/optimizations/all-optimizations-test.ts: -------------------------------------------------------------------------------- 1 | import { TestTemplate } from "@opticss/simple-template"; 2 | import { assert } from "chai"; 3 | import { 4 | skip, 5 | suite, 6 | test, 7 | } from "mocha-typescript"; 8 | 9 | import { 10 | CascadeTestResult, 11 | debugError, 12 | testOptimizationCascade, 13 | } from "../util/assertCascade"; 14 | import { assertSmaller } from "../util/assertSmaller"; 15 | import { clean } from "../util/clean"; 16 | 17 | function testAll(...stylesAndTemplates: Array): Promise { 18 | return testOptimizationCascade( 19 | { }, 20 | { 21 | rewriteIdents: { id: true, class: true }, 22 | analyzedAttributes: [], 23 | analyzedTagnames: true, 24 | }, 25 | ...stylesAndTemplates); 26 | } 27 | 28 | @suite("Running All Optimizations") 29 | export class MergeDeclarationsTest { 30 | @test "on two sibling elements"() { 31 | let css1 = clean` 32 | #first { float: left;} 33 | #second { float: right;} 34 | .asdf { color: red; } 35 | .wsx { border: 1px solid blue; } 36 | .qwerty { background: red; } 37 | .qaz { border-color: blue; color: red; } 38 | #third { clear: both; position: relative; } 39 | `; 40 | let template = new TestTemplate("test", clean` 41 |
42 |
43 | `); 44 | return testAll(css1, template).then(result => { 45 | assert.deepEqual(result.optimization.output.content.toString(), clean` 46 | #a { float: left;} 47 | #b { float: right;} 48 | .b { color: red; } 49 | .c { border: 1px solid blue; } 50 | .d { background: red; } 51 | .e { border-color: blue; } 52 | #c { clear: both; position: relative; } 53 | `); 54 | // debugResult(css1, result); 55 | return assertSmaller(css1, result); 56 | }).catch(error => { 57 | debugError(css1, error); 58 | throw error; 59 | }); 60 | } 61 | 62 | @skip 63 | @test "mergeable decls with ids"() { 64 | let css1 = clean` 65 | #first { color: red; } 66 | .asdf { border: 1px solid blue; } 67 | .wsx { border-width: 1px; } 68 | .qwerty { border-color: blue; color: red; } 69 | .qaz { border-style: solid; } 70 | `; 71 | let template = new TestTemplate("test", clean` 72 |
73 |
74 | `); 75 | return testAll(css1, template).then(result => { 76 | assert.deepEqual(result.optimization.output.content.toString(), clean` 77 | `); 78 | // debugResult(css1, result); 79 | return assertSmaller(css1, result); 80 | }).catch(error => { 81 | debugError(css1, error); 82 | throw error; 83 | }); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /packages/opticss/test/parse-selector-test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | } from "chai"; 4 | import { 5 | skip, 6 | suite, 7 | test, 8 | } from "mocha-typescript"; 9 | import selectorParser = require("postcss-selector-parser"); 10 | 11 | import { parseSelector } from "../src/parseSelector"; 12 | 13 | // import assertError from "./util/assertError"; 14 | 15 | @suite("parseSelector") 16 | export class ParseSelectorTests { 17 | 18 | @test "handles string input"() { 19 | let selector = ".foo .bar, .biz .baz"; 20 | let res = parseSelector(selector); 21 | assert.equal(res.length, 2); 22 | assert.equal(res[0].length, 2); 23 | } 24 | 25 | @test "handles pseudo elements"() { 26 | let selector = "::selection"; 27 | let res = parseSelector(selector); 28 | assert.equal(res.length, 1); 29 | assert.equal(res[0].length, 1); 30 | assert.isDefined(res[0].selector); 31 | } 32 | 33 | @test "handles selectorParser.Root"() { 34 | let selector = selectorParser().astSync(".foo .bar, .biz .baz"); 35 | let res = parseSelector(selector); 36 | 37 | assert.equal(res.length, 2); 38 | assert.equal(res[0].length, 2); 39 | } 40 | 41 | // Theres something weird going on here 42 | @test @skip "handles selectorParser.Node[]"() { 43 | let selector = selectorParser().astSync(".foo .bar, .biz .baz").nodes; 44 | let res = parseSelector(selector); 45 | assert.equal(res.length, 2); 46 | assert.equal(res[0].length, 2); 47 | } 48 | 49 | // Theres something weird going on here 50 | @test @skip "handles selectorParser.Node[][]"() { 51 | let selector = [ selectorParser().astSync(".foo .bar").nodes, selectorParser().astSync(".biz .baz").nodes ]; 52 | let res = parseSelector(selector); 53 | assert.equal(res.length, 2); 54 | assert.equal(res[0].length, 2); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /packages/opticss/test/util/clean.ts: -------------------------------------------------------------------------------- 1 | export function clean(strings: TemplateStringsArray, ...expressions: string[]) { 2 | let str = strings.reduce( 3 | (prev, s, i) => prev + s + ((expressions.length > i) ? expressions[i].toString() : ""), 4 | ""); 5 | return str.split("\n").map(s => s.trim()).join("\n").trim(); 6 | } 7 | -------------------------------------------------------------------------------- /packages/opticss/test/util/input.txt: -------------------------------------------------------------------------------- 1 | \( 2 | -------------------------------------------------------------------------------- /packages/opticss/test/util/randomness.ts: -------------------------------------------------------------------------------- 1 | import { MersenneTwister19937, Random, nativeMath } from "random-js"; 2 | 3 | export function getRandom(opts: { seed?: number; verbose?: boolean}): Random { 4 | const seed = opts.seed || nativeMath.next(); 5 | if (opts.verbose) { 6 | // tslint:disable-next-line:no-console 7 | console.log(`Random seed is: ${seed}`); 8 | } 9 | let engine = MersenneTwister19937.seed(seed); 10 | return new Random(engine); 11 | } 12 | -------------------------------------------------------------------------------- /packages/opticss/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "baseUrl": "dist", 6 | "types": [ 7 | "@types/chai", 8 | "@types/debug", 9 | "@types/mocha", 10 | "@types/node" 11 | ], 12 | "paths": { 13 | "concat-with-sourcemaps": ["../types-local/concat-with-sourcemaps"], 14 | "parse5": ["../../@opticss/simple-template/types-local/parse5/index.d.ts"] 15 | } 16 | }, 17 | "include": [ 18 | "src", 19 | "test" 20 | ], 21 | "exclude": [ 22 | "dist", 23 | "node_modules" 24 | ], 25 | "references": [ 26 | {"path": "../@opticss/element-analysis"}, 27 | {"path": "../@opticss/template-api"}, 28 | {"path": "../@opticss/util"} 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /packages/opticss/tslint.cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.cli.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/opticss/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@opticss/code-style/configs/tslint.interactive.json" 3 | } -------------------------------------------------------------------------------- /packages/opticss/tslint.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.release.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/opticss/types-local/concat-with-sourcemaps/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "concat-with-sourcemaps" { 2 | import * as sourcemap from 'source-map'; 3 | class Concat { 4 | readonly content: Buffer; 5 | readonly sourceMap: string | undefined; 6 | constructor(generateSourceMap: boolean, fileName: string, separator?: string); 7 | add(fileName: string | null, content: string | Buffer, sourceMap: string | sourcemap.RawSourceMap): void; 8 | } 9 | export = Concat; 10 | } -------------------------------------------------------------------------------- /packages/opticss/types-local/concat-with-sourcemaps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": {}, 3 | "description": "TypeScript definitions for concat-with-sourcemaps 1.0.4", 4 | "name": "@types/concat-with-sourcemaps", 5 | "typings": "index.d.ts", 6 | "version": "1.0.4" 7 | } -------------------------------------------------------------------------------- /packages/opticss/types-local/css-size/index.d.ts: -------------------------------------------------------------------------------- 1 | export = cssSize; 2 | 3 | 4 | declare function cssSize( 5 | css: string, 6 | options: cssSize.ProcessOptions, 7 | processor?: cssSize.Processor 8 | ): Promise>; 9 | 10 | declare namespace cssSize { 11 | function table( 12 | css: string, 13 | options: ProcessOptions, 14 | processor?: Processor 15 | ): Promise; 16 | 17 | function numeric( 18 | css: string, 19 | options: ProcessOptions, 20 | processor?: Processor 21 | ): Promise>; 22 | 23 | interface HasCss { 24 | css: string; 25 | } 26 | interface ProcessOptions { 27 | [opt: string]: any; 28 | } 29 | type Processor = (css: string, options: ProcessOptions) => Promise; 30 | 31 | /** 32 | * The size before and after, the absolute different and the percent improvement. 33 | */ 34 | interface SizeInfo { 35 | original: T; 36 | processed: T; 37 | difference: T; 38 | percent: T; 39 | } 40 | 41 | /** 42 | * Size deltas of the css in various formats. 43 | */ 44 | interface Result { 45 | uncompressed: SizeInfo; 46 | gzip: SizeInfo; 47 | brotli: SizeInfo; 48 | } 49 | } -------------------------------------------------------------------------------- /packages/opticss/types-local/css-size/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": {}, 3 | "description": "TypeScript definitions for concat-with-sourcemaps 1.0.4", 4 | "name": "@types/css-size", 5 | "typings": "index.d.ts", 6 | "version": "4.0.0" 7 | } -------------------------------------------------------------------------------- /packages/resolve-cascade/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | test/ 3 | ts*.json 4 | -------------------------------------------------------------------------------- /packages/resolve-cascade/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "preLaunchTask": "compile", 12 | "program": "${workspaceRoot}/node_modules/.bin/_mocha", 13 | "args": [ 14 | "dist/test", 15 | "--opts", 16 | "test/mocha.opts" 17 | ], 18 | "cwd": "${workspaceRoot}", 19 | "outFiles": [ 20 | "${workspaceRoot}/dist/**/*.js" 21 | ], 22 | "sourceMaps": true 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/resolve-cascade/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "**/dist/**/*": true, 5 | "**/*.lock": true 6 | }, 7 | "editor.tabSize": 2, 8 | "typescriptHero.codeOutline.enabled": true, 9 | "typescriptHero.resolver.multiLineWrapThreshold": 10, 10 | "typescriptHero.resolver.multiLineTrailingComma": true, 11 | "typescriptHero.resolver.organizeOnSave": true, 12 | "tslint.configFile": "tslint.json", 13 | "tslint.enable": true 14 | } -------------------------------------------------------------------------------- /packages/resolve-cascade/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "0.1.0", 5 | "command": "npm", 6 | "isShellCommand": true, 7 | "showOutput": "always", 8 | "suppressTaskName": true, 9 | "tasks": [ 10 | { 11 | "taskName": "install", 12 | "args": ["install"] 13 | }, 14 | { 15 | "taskName": "update", 16 | "args": ["update"] 17 | }, 18 | { 19 | "taskName": "test", 20 | "args": ["run", "test"] 21 | }, 22 | { 23 | "taskName": "builder", 24 | "isBuildCommand": true, 25 | "args": ["run", "test"] 26 | }, 27 | { 28 | "taskName": "compile", 29 | "args": ["run", "prepublishOnly"] 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /packages/resolve-cascade/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.6.2](https://github.com/linkedin/opticss/compare/resolve-cascade@0.6.1...resolve-cascade@0.6.2) (2019-12-09) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * Silence postcss warning. ([f881da8](https://github.com/linkedin/opticss/commit/f881da8)) 12 | 13 | 14 | 15 | 16 | 17 | ## [0.6.1](https://github.com/linkedin/opticss/compare/resolve-cascade@0.6.0...resolve-cascade@0.6.1) (2019-04-30) 18 | 19 | 20 | ### Bug Fixes 21 | 22 | * Upgrade parse5 to 5.0.0 to enable browser builds. ([6a88a33](https://github.com/linkedin/opticss/commit/6a88a33)) 23 | 24 | 25 | 26 | 27 | 28 | # [0.6.0](https://github.com/linkedin/opticss/compare/resolve-cascade@0.5.0...resolve-cascade@0.6.0) (2019-04-25) 29 | 30 | **Note:** Version bump only for package resolve-cascade 31 | 32 | 33 | 34 | 35 | 36 | # 0.5.0 (2019-04-19) 37 | 38 | 39 | 40 | # 0.4.0 (2018-10-19) 41 | 42 | 43 | ### Features 44 | 45 | * Manually throw error for Node 6 in Optimizer. ([c4db789](https://github.com/linkedin/opticss/commit/c4db789)) 46 | 47 | 48 | 49 | # 0.3.0 (2018-04-24) 50 | 51 | 52 | 53 | # 0.3.0-rc.0 (2018-04-24) 54 | 55 | 56 | 57 | ## 0.1.1 (2017-11-25) 58 | 59 | 60 | 61 | 62 | 63 | 64 | # [0.3.0](https://github.com/linkedin/opticss/compare/v0.3.0-rc.0...v0.3.0) (2018-04-24) 65 | 66 | **Note:** Version bump only for package resolve-cascade 67 | -------------------------------------------------------------------------------- /packages/resolve-cascade/README.md: -------------------------------------------------------------------------------- 1 | # `resolve-cascade` 2 | 3 | Resolves a CSS Stylesheet against an HTML Document producing 4 | a Cascade of selectors for each element. A Cascade can then 5 | be used to produce a computed style for that element. 6 | 7 | In addition to libraries for performing cascade resolution, 8 | there's two assertions for help in writing tests that 9 | check the expected effects of CSS on a document. 10 | 11 | [API documentation](./docs/README.md) 12 | -------------------------------------------------------------------------------- /packages/resolve-cascade/docs/classes/resolve_cascade.cascade.md: -------------------------------------------------------------------------------- 1 | [resolve-cascade](../README.md) > [Cascade](../classes/resolve_cascade.cascade.md) 2 | 3 | # Class: Cascade 4 | 5 | ## Hierarchy 6 | 7 | **Cascade** 8 | 9 | ## Index 10 | 11 | ### Constructors 12 | 13 | * [constructor](resolve_cascade.cascade.md#constructor) 14 | 15 | ### Properties 16 | 17 | * [html](resolve_cascade.cascade.md#html) 18 | * [stylesheet](resolve_cascade.cascade.md#stylesheet) 19 | 20 | ### Methods 21 | 22 | * [perform](resolve_cascade.cascade.md#perform) 23 | 24 | --- 25 | 26 | ## Constructors 27 | 28 | 29 | 30 | ### ⊕ **new Cascade**(stylesheet: *`string`*, html: *`Document`*): [Cascade](resolve_cascade.cascade.md) 31 | 32 | *Defined in [src/Cascade.ts:153](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/Cascade.ts#L153)* 33 | 34 | **Parameters:** 35 | 36 | | Param | Type | Description | 37 | | ------ | ------ | ------ | 38 | | stylesheet | `string` | - | 39 | | html | `Document` | - | 40 | 41 | **Returns:** [Cascade](resolve_cascade.cascade.md) 42 | 43 | --- 44 | 45 | ## Properties 46 | 47 | 48 | 49 | ### html 50 | 51 | **● html**: *`Document`* 52 | 53 | *Defined in [src/Cascade.ts:153](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/Cascade.ts#L153)* 54 | 55 | ___ 56 | 57 | 58 | 59 | ### stylesheet 60 | 61 | **● stylesheet**: *`string`* 62 | 63 | *Defined in [src/Cascade.ts:152](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/Cascade.ts#L152)* 64 | 65 | ___ 66 | 67 | ## Methods 68 | 69 | 70 | 71 | ### perform 72 | 73 | ▸ **perform**(): `Promise`.<[FullCascade](../#fullcascade)> 74 | 75 | *Defined in [src/Cascade.ts:158](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/Cascade.ts#L158)* 76 | 77 | **Returns:** `Promise`.<[FullCascade](../#fullcascade)> 78 | 79 | ___ 80 | 81 | -------------------------------------------------------------------------------- /packages/resolve-cascade/docs/interfaces/resolve_cascade.assertionresult.md: -------------------------------------------------------------------------------- 1 | [resolve-cascade](../README.md) > [AssertionResult](../interfaces/resolve_cascade.assertionresult.md) 2 | 3 | # Interface: AssertionResult 4 | 5 | ## Hierarchy 6 | 7 | **AssertionResult** 8 | 9 | ## Index 10 | 11 | ### Properties 12 | 13 | * [actualCss](resolve_cascade.assertionresult.md#actualcss) 14 | * [actualDoc](resolve_cascade.assertionresult.md#actualdoc) 15 | * [actualFullCascade](resolve_cascade.assertionresult.md#actualfullcascade) 16 | * [expectedCss](resolve_cascade.assertionresult.md#expectedcss) 17 | * [expectedDoc](resolve_cascade.assertionresult.md#expecteddoc) 18 | * [expectedFullCascade](resolve_cascade.assertionresult.md#expectedfullcascade) 19 | 20 | --- 21 | 22 | ## Properties 23 | 24 | 25 | 26 | ### actualCss 27 | 28 | **● actualCss**: *`string`* 29 | 30 | *Defined in [src/assertCascade.ts:25](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/assertCascade.ts#L25)* 31 | 32 | ___ 33 | 34 | 35 | 36 | ### actualDoc 37 | 38 | **● actualDoc**: *`Document`* 39 | 40 | *Defined in [src/assertCascade.ts:24](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/assertCascade.ts#L24)* 41 | 42 | ___ 43 | 44 | 45 | 46 | ### actualFullCascade 47 | 48 | **● actualFullCascade**: *[FullCascade](../#fullcascade)* 49 | 50 | *Defined in [src/assertCascade.ts:23](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/assertCascade.ts#L23)* 51 | 52 | ___ 53 | 54 | 55 | 56 | ### expectedCss 57 | 58 | **● expectedCss**: *`string`* 59 | 60 | *Defined in [src/assertCascade.ts:22](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/assertCascade.ts#L22)* 61 | 62 | ___ 63 | 64 | 65 | 66 | ### expectedDoc 67 | 68 | **● expectedDoc**: *`Document`* 69 | 70 | *Defined in [src/assertCascade.ts:21](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/assertCascade.ts#L21)* 71 | 72 | ___ 73 | 74 | 75 | 76 | ### expectedFullCascade 77 | 78 | **● expectedFullCascade**: *[FullCascade](../#fullcascade)* 79 | 80 | *Defined in [src/assertCascade.ts:20](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/assertCascade.ts#L20)* 81 | 82 | ___ 83 | 84 | -------------------------------------------------------------------------------- /packages/resolve-cascade/docs/interfaces/resolve_cascade.computedstyle.md: -------------------------------------------------------------------------------- 1 | [resolve-cascade](../README.md) > [ComputedStyle](../interfaces/resolve_cascade.computedstyle.md) 2 | 3 | # Interface: ComputedStyle 4 | 5 | The styles computed from the cascade for an element in a particular state. 6 | 7 | ## Hierarchy 8 | 9 | **ComputedStyle** 10 | 11 | ## Indexable 12 | 13 | \[property: `string`\]: `string` 14 | The styles computed from the cascade for an element in a particular state. 15 | 16 | ## Index 17 | 18 | --- 19 | 20 | -------------------------------------------------------------------------------- /packages/resolve-cascade/docs/interfaces/resolve_cascade.matchedselector.md: -------------------------------------------------------------------------------- 1 | [resolve-cascade](../README.md) > [MatchedSelector](../interfaces/resolve_cascade.matchedselector.md) 2 | 3 | # Interface: MatchedSelector 4 | 5 | ## Hierarchy 6 | 7 | **MatchedSelector** 8 | 9 | ## Index 10 | 11 | ### Properties 12 | 13 | * [rule](resolve_cascade.matchedselector.md#rule) 14 | * [selector](resolve_cascade.matchedselector.md#selector) 15 | * [specificity](resolve_cascade.matchedselector.md#specificity) 16 | 17 | --- 18 | 19 | ## Properties 20 | 21 | 22 | 23 | ### rule 24 | 25 | **● rule**: *`Rule`* 26 | 27 | *Defined in [src/Cascade.ts:37](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/Cascade.ts#L37)* 28 | 29 | ___ 30 | 31 | 32 | 33 | ### selector 34 | 35 | **● selector**: *`string`* 36 | 37 | *Defined in [src/Cascade.ts:36](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/Cascade.ts#L36)* 38 | 39 | ___ 40 | 41 | 42 | 43 | ### specificity 44 | 45 | **● specificity**: *`Specificity`* 46 | 47 | *Defined in [src/Cascade.ts:38](https://github.com/linkedin/opticss/blob/d5d95b5/packages/resolve-cascade/src/Cascade.ts#L38)* 48 | 49 | ___ 50 | 51 | -------------------------------------------------------------------------------- /packages/resolve-cascade/docs/interfaces/resolve_cascade.pseudostates.md: -------------------------------------------------------------------------------- 1 | [resolve-cascade](../README.md) > [PseudoStates](../interfaces/resolve_cascade.pseudostates.md) 2 | 3 | # Interface: PseudoStates 4 | 5 | ## Hierarchy 6 | 7 | **PseudoStates** 8 | 9 | ## Indexable 10 | 11 | \[pseudoState: `string`\]: `Array`.<`Element`> 12 | Maps the pseudo-state's name to the elements for which that state can affect the computed style of an element. 13 | 14 | ## Index 15 | 16 | --- 17 | 18 | -------------------------------------------------------------------------------- /packages/resolve-cascade/docs/interfaces/resolve_cascade.styledpseudoelements.md: -------------------------------------------------------------------------------- 1 | [resolve-cascade](../README.md) > [StyledPseudoElements](../interfaces/resolve_cascade.styledpseudoelements.md) 2 | 3 | # Interface: StyledPseudoElements 4 | 5 | ## Hierarchy 6 | 7 | **StyledPseudoElements** 8 | 9 | ## Indexable 10 | 11 | \[pseudoElement: `string`\]: [ElementStyle](../classes/resolve_cascade.elementstyle.md) 12 | Maps the pseudo-element's name to its computed style The name does not include the preceding colon(s). 13 | 14 | ## Index 15 | 16 | --- 17 | 18 | -------------------------------------------------------------------------------- /packages/resolve-cascade/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "resolve-cascade", 3 | "version": "0.6.2", 4 | "description": "Resolve CSS selectors against a document.", 5 | "main": "dist/src/index.js", 6 | "types": "dist/src/index.d.ts", 7 | "scripts": { 8 | "compile": "tsc --build", 9 | "pretest": "yarn run compile", 10 | "posttest": "yarn run lint", 11 | "prepublishOnly": "yarn run compile && yarn run lintall", 12 | "lint": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json", 13 | "lintall": "tslint -t msbuild --project tsconfig.json -c tslint.release.json", 14 | "lintfix": "tslint -t msbuild --project tsconfig.json -c tslint.cli.json --fix", 15 | "test": "mocha dist/test --opts test/mocha.opts", 16 | "coverage": "istanbul cover -i \"dist/src/**/*.js\" --dir ./build/coverage _mocha -- dist/test --opts test/mocha.opts", 17 | "remap": "remap-istanbul -i build/coverage/coverage.json -o coverage -t html", 18 | "docs": "typedoc --readme none --excludeExternals --theme markdown --out ./docs --exclude src/util.ts --entryPoint resolve-cascade ." 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/linkedin/opticss.git" 23 | }, 24 | "keywords": [ 25 | "css", 26 | "cascade" 27 | ], 28 | "author": "Chris Eppstein", 29 | "license": "BSD-2-Clause", 30 | "bugs": { 31 | "url": "https://github.com/linkedin/opticss/issues?q=is%3Aopen+is%3Aissue+label%3Apkg%3Aresolve-cascade" 32 | }, 33 | "homepage": "https://github.com/linkedin/opticss/tree/master/packages/resolve-cascade#readme", 34 | "engines": { 35 | "node": "6.* || 8.* || >= 10.*" 36 | }, 37 | "publishConfig": { 38 | "access": "public" 39 | }, 40 | "devDependencies": { 41 | "@opticss/code-style": "^0.6.0", 42 | "@types/chai": "^4.0.4", 43 | "@types/mocha": "^5.2.6", 44 | "@types/node": "^11.13.8", 45 | "@types/parse5": "^5.0.0", 46 | "chai": "^4.1.2", 47 | "istanbul": "^0.4.5", 48 | "mocha": "^6.1.4", 49 | "mocha-typescript": "^1.1.9", 50 | "remap-istanbul": "^0.13.0", 51 | "source-map-support": "^0.5.3", 52 | "tslint": "^5.1.0", 53 | "typedoc": "^0.15.0-0", 54 | "typedoc-plugin-external-module-name": "^2.0.0", 55 | "typedoc-plugin-markdown": "^1.1.8", 56 | "typescript": "~3.4.4" 57 | }, 58 | "dependencies": { 59 | "@opticss/css-select": "1.3.0-li.3", 60 | "css-property-parser": "^1.0.5", 61 | "css-size": "^4.0.1", 62 | "domutils": "^1.6.2", 63 | "parse5": "^5.0.0", 64 | "postcss": "^7.0.14", 65 | "specificity": "^0.4.1" 66 | }, 67 | "gitHead": "ef310cb1b10dbc90cae4f859da146863f99d940b" 68 | } 69 | -------------------------------------------------------------------------------- /packages/resolve-cascade/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module resolve-cascade 3 | **/ 4 | 5 | /* tslint:disable */ 6 | // Object.entries polyfill for Node.js 6 7 | // TODO: Remove April 2019 when Node.js 6 is EOL'd 8 | if (!(Object as any).entries) { 9 | (Object as any).entries = function(obj: any){ 10 | let ownProps = Object.keys(obj), i = ownProps.length, resArray = new Array(i); 11 | while (i--) resArray[i] = [ownProps[i], obj[ownProps[i]]]; 12 | return resArray; 13 | }; 14 | } 15 | /* tslint:enable */ 16 | 17 | export * from "./Cascade"; 18 | export * from "./util"; 19 | export * from "./assertCascade"; 20 | -------------------------------------------------------------------------------- /packages/resolve-cascade/test/assert-cascade-test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | } from "chai"; 4 | import { 5 | suite, 6 | test, 7 | } from "mocha-typescript"; 8 | 9 | import { 10 | assertSameCascade, 11 | } from "../src"; 12 | 13 | import { clean } from "./clean"; 14 | 15 | @suite("Cascade") 16 | export class CascadeTest { 17 | @test "can assert cascades match"() { 18 | let expectedCss = clean` 19 | #c { background-color: #F00; } 20 | .a { color: red; } 21 | .b { background: none; } 22 | `; 23 | let expectedHtml = clean` 24 |
25 | `; 26 | let actualCss = clean` 27 | .everything { 28 | background: none #F00; 29 | color: red; 30 | } 31 | `; 32 | let actualHtml = clean` 33 |
34 | `; 35 | 36 | return assertSameCascade(expectedCss, actualCss, expectedHtml, actualHtml).then(result => { 37 | result.actualFullCascade.forEach(element => { 38 | let style = element.compute(); 39 | assert.equal(style.color, "red"); 40 | }); 41 | }); 42 | } 43 | @test "initial values are equivalent"() { 44 | let expectedCss = clean` 45 | .a { position: static; } 46 | .b { background: none; } 47 | `; 48 | let expectedHtml = clean` 49 |
50 |
51 | `; 52 | let actualCss = clean` 53 | .a { position: initial; } 54 | .b { background: none transparent; } 55 | `; 56 | let actualHtml = clean` 57 |
58 |
59 | `; 60 | 61 | return assertSameCascade(expectedCss, actualCss, 62 | expectedHtml, actualHtml).then(() => {}); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /packages/resolve-cascade/test/cascade-test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | } from "chai"; 4 | import { 5 | suite, 6 | test, 7 | } from "mocha-typescript"; 8 | 9 | import { 10 | Cascade, 11 | } from "../src/Cascade"; 12 | import { 13 | bodyElement, 14 | parseHtml, 15 | walkElements, 16 | } from "../src/util"; 17 | 18 | import { clean } from "./clean"; 19 | 20 | @suite("Cascade") 21 | export class CascadeTest { 22 | @test "can compute a simple style"() { 23 | let css = clean` 24 | .a { color: red; } 25 | `; 26 | let html = clean` 27 |
28 | `; 29 | let document = parseHtml(html); 30 | let body = bodyElement(document)!; 31 | let cascade = new Cascade(css, document); 32 | return cascade.perform().then(cascadedStyles => { 33 | walkElements(body, (node) => { 34 | let elStyle = cascadedStyles.get(node); 35 | if (elStyle) { 36 | let styles = elStyle.compute(); 37 | assert.equal(styles.color, "red"); 38 | } 39 | }); 40 | }); 41 | } 42 | @test "can be constructed"() { 43 | let css = clean` 44 | #c { background-color: #F00; } 45 | .a { color: red; } 46 | .b { background: none; } 47 | `; 48 | let html = clean` 49 |
50 | `; 51 | let document = parseHtml(html); 52 | let body = bodyElement(document)!; 53 | let cascade = new Cascade(css, document); 54 | return cascade.perform().then(cascadedStyles => { 55 | walkElements(body, (node) => { 56 | let elStyle = cascadedStyles.get(node); 57 | if (elStyle) { 58 | let styles = elStyle.compute(); 59 | assert.deepEqual(styles, { 60 | "background-attachment": "scroll", 61 | "background-clip": "border-box", 62 | "background-color": "#F00", 63 | "background-image": "none", 64 | "background-origin": "padding-box", 65 | "background-position": "0% 0%", 66 | "background-repeat": "repeat", 67 | "background-size": "auto auto", 68 | "color": "red", 69 | }); 70 | } 71 | }); 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /packages/resolve-cascade/test/clean.ts: -------------------------------------------------------------------------------- 1 | export function clean(strings: TemplateStringsArray, ...expressions: string[]) { 2 | let str = strings.reduce( 3 | (prev, s, i) => 4 | prev + s + ((expressions.length > i) ? expressions[i].toString() : ""), 5 | ""); 6 | return str.split("\n").map(s => s.trim()).join("\n").trim(); 7 | } 8 | -------------------------------------------------------------------------------- /packages/resolve-cascade/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --require source-map-support/register 3 | --inline-diffs 4 | 5 | -------------------------------------------------------------------------------- /packages/resolve-cascade/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "baseUrl": "dist", 6 | "types": [ 7 | "@types/chai", 8 | "@types/node", 9 | "@types/mocha" 10 | ], 11 | "paths": { 12 | "parse5": ["../types-local/parse5/index.d.ts"], 13 | "parse5-sax-parser": ["../types-local/parse5-sax-parser/index.d.ts"] 14 | } 15 | }, 16 | "include": [ 17 | "src", 18 | "test" 19 | ], 20 | "exclude": [ 21 | "dist", 22 | "node_modules" 23 | ], 24 | "references": [ 25 | {"path": "../@opticss/code-style"} 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /packages/resolve-cascade/tslint.cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.cli.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/resolve-cascade/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@opticss/code-style/configs/tslint.interactive.json" 3 | } -------------------------------------------------------------------------------- /packages/resolve-cascade/tslint.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tslint", 3 | "extends": "@opticss/code-style/configs/tslint.release.json" 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Compilation Configuration 4 | "composite": true, 5 | "target": "es2015", 6 | "inlineSources": false, 7 | "inlineSourceMap": true, 8 | "declaration": true, 9 | "declarationMap": true, 10 | "allowSyntheticDefaultImports": true, 11 | 12 | // Environment Configuration 13 | "experimentalDecorators": true, 14 | "moduleResolution": "node", 15 | 16 | // Enhance Strictness 17 | "noImplicitAny": true, 18 | "suppressImplicitAnyIndexErrors": true, 19 | "noUnusedParameters": true, 20 | "allowUnreachableCode": false, 21 | "strictNullChecks": true, 22 | "noImplicitReturns": true, 23 | "noImplicitThis": true, 24 | "preserveConstEnums": true, 25 | "forceConsistentCasingInFileNames": true, 26 | "noUnusedLocals": true, 27 | "alwaysStrict": true, 28 | "lib": [ 29 | "es2016" 30 | ], 31 | 32 | // output options 33 | "newLine": "LF", 34 | "module": "commonjs", 35 | "traceResolution": false 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "curly": false, 4 | "no-var-keyword": true, 5 | "indent": [true, "spaces"], 6 | "label-position": true, 7 | "no-consecutive-blank-lines": [ 8 | true 9 | ], 10 | "no-construct": true, 11 | "no-debugger": true, 12 | "no-duplicate-variable": true, 13 | "no-inferrable-types": [true], 14 | "no-trailing-whitespace": true, 15 | "no-unused-expression": true, 16 | "semicolon": [true, "always"], 17 | "triple-equals": true, 18 | "class-name": true 19 | } 20 | } 21 | --------------------------------------------------------------------------------