├── .all-contributorsrc ├── .changeset ├── README.md └── config.json ├── .codecov.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .git-blame-ignore-revs ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── enhancement_request.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── labels.json └── workflows │ ├── check.yml │ ├── main.yml │ ├── release.yml │ └── site-deploy.yml ├── .gitignore ├── .npmrc ├── .prettierrc ├── .syncpackrc.js ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── babel.config.js ├── commitlint.config.js ├── docs ├── API.md ├── ATOMIC_CSS.md ├── BASICS.md ├── BENEFITS.md ├── BUNDLERS_INTEGRATION.md ├── CLI.md ├── CONFIGURATION.md ├── CRITICAL_CSS.md ├── DYNAMIC_STYLES.md ├── FEATURE_FLAGS.md ├── HOW_IT_WORKS.md ├── LINTING.md ├── MIGRATION_GUIDE.md └── THEMING.md ├── examples ├── astro-solid │ ├── astro.config.js │ ├── package.json │ ├── pages │ │ ├── csr.tsx │ │ ├── csr_child.tsx │ │ ├── external.ts │ │ └── index.astro │ └── tsconfig.json ├── esbuild │ ├── .gitignore │ ├── app.js │ ├── build.js │ └── package.json ├── rollup │ ├── .gitignore │ ├── app.js │ ├── babel.config.js │ ├── package.json │ └── rollup.config.mjs ├── vite │ ├── .linariarc.mjs │ ├── app.js │ ├── index.html │ ├── package.json │ └── vite.config.js ├── vpssr-linaria-solid │ ├── package.json │ ├── pages │ │ ├── html-js │ │ │ ├── _default.page.client.js │ │ │ └── index.page.server.tsx │ │ ├── html-only │ │ │ └── index.page.server.tsx │ │ ├── index.page.server.tsx │ │ ├── spa │ │ │ └── index.page.client.tsx │ │ └── ssr │ │ │ ├── Counter.tsx │ │ │ └── index.page.tsx │ ├── renderer │ │ ├── _default.page.client.tsx │ │ └── _default.page.server.tsx │ ├── server.js │ ├── tsconfig.json │ └── vite.config.js └── webpack5 │ ├── app.js │ ├── babel.config.js │ ├── index.html │ ├── package.json │ └── webpack.config.js ├── greenkeeper.json ├── jest.config.js ├── link-wyw.sh ├── package.json ├── packages ├── atomic │ ├── CHANGELOG.md │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── processors │ │ ├── css.js │ │ └── styled.js │ ├── src │ │ ├── CSSProperties.ts │ │ ├── css.ts │ │ ├── index.ts │ │ └── processors │ │ │ ├── css.ts │ │ │ ├── helpers │ │ │ ├── atomize.ts │ │ │ └── propertyPriority.ts │ │ │ └── styled.ts │ └── tsconfig.json ├── core │ ├── CHANGELOG.md │ ├── README.md │ ├── __dtslint__ │ │ ├── core.ts │ │ ├── index.d.ts │ │ ├── tsconfig.eslint.json │ │ └── tsconfig.json │ ├── __tests__ │ │ ├── cx.test.ts │ │ └── detect-core-js.test.ts │ ├── babel.config.js │ ├── package.json │ ├── processors │ │ └── css.js │ ├── src │ │ ├── CSSProperties.ts │ │ ├── css.ts │ │ ├── cx.ts │ │ ├── index.ts │ │ └── processors │ │ │ └── css.ts │ └── tsconfig.json ├── interop │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── index.test.ts.snap │ │ └── index.test.ts │ ├── babel.config.js │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── linaria │ ├── CHANGELOG.md │ ├── README.md │ ├── babel.config.js │ ├── babel │ │ └── package.json │ ├── evaluators │ │ └── package.json │ ├── loader │ │ └── package.json │ ├── package.json │ ├── react │ │ └── package.json │ ├── rollup │ │ └── package.json │ ├── server │ │ └── package.json │ ├── src │ │ ├── core.ts │ │ ├── react.ts │ │ └── server.ts │ ├── stylelint-config │ │ └── package.json │ └── tsconfig.json ├── postcss-linaria │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__ │ │ ├── __utils__ │ │ │ └── index.ts │ │ ├── locationCorrection.test.ts │ │ ├── parse.test.ts │ │ ├── stringify.test.ts │ │ ├── stylelint.test.ts │ │ └── utils.test.ts │ ├── babel.config.js │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── locationCorrection.ts │ │ ├── parse.ts │ │ ├── stringify.ts │ │ └── util.ts │ └── tsconfig.json ├── react │ ├── CHANGELOG.md │ ├── README.md │ ├── __dtslint__ │ │ ├── index.d.ts │ │ ├── styled.ts │ │ ├── tsconfig.eslint.json │ │ └── tsconfig.json │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── styled.test.tsx.snap │ │ ├── detect-core-js.test.ts │ │ └── styled.test.tsx │ ├── babel.config.js │ ├── package.json │ ├── processors │ │ └── styled.js │ ├── src │ │ ├── index.ts │ │ ├── processors │ │ │ └── styled.ts │ │ ├── react-html-attributes.d.ts │ │ └── styled.ts │ └── tsconfig.json ├── server │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── collect.test.ts.snap │ │ └── collect.test.ts │ ├── babel.config.js │ ├── package.json │ ├── src │ │ ├── collect.ts │ │ └── index.ts │ └── tsconfig.json ├── stylelint-config-standard-linaria │ ├── CHANGELOG.md │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── src │ │ └── index.js │ └── tsconfig.json ├── stylelint │ ├── CHANGELOG.md │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── preprocessor.ts │ └── tsconfig.json └── testkit │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── src │ ├── __fixtures__ │ │ ├── assignToExport.js │ │ ├── bar.js │ │ ├── circular-imports │ │ │ ├── bar.js │ │ │ ├── constants.js │ │ │ ├── foo.js │ │ │ └── index.js │ │ ├── complex-component.js │ │ ├── components-library.js │ │ ├── computedKeys.js │ │ ├── enums.ts │ │ ├── escape-character.js │ │ ├── foo.js │ │ ├── linaria-ui-library │ │ │ ├── components │ │ │ │ └── index.js │ │ │ ├── hocs.js │ │ │ ├── non-linaria-components.js │ │ │ ├── package.json │ │ │ └── types.ts │ │ ├── loop │ │ │ ├── a.js │ │ │ ├── ab.js │ │ │ ├── b.js │ │ │ ├── ba.js │ │ │ └── index.js │ │ ├── module-reexport.js │ │ ├── non-linaria-ui-library │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── objectExport.js │ │ ├── re-exports │ │ │ ├── constants.js │ │ │ ├── empty.js │ │ │ ├── foo.js │ │ │ └── index.js │ │ ├── reexports.js │ │ ├── runNearFramePaint.js │ │ ├── sample-asset.png │ │ ├── sample-data.json │ │ ├── sample-script.cjs │ │ ├── sample-script.js │ │ ├── sample-typescript.tsx │ │ ├── self-import.js │ │ ├── sequenceExport.js │ │ ├── slugify.js │ │ ├── ts-compiled-re-exports │ │ │ ├── constants.js │ │ │ └── index.js │ │ ├── ts-data.ts │ │ └── with-babelrc │ │ │ ├── .babelrc.js │ │ │ └── index.js │ ├── __snapshots__ │ │ ├── babel.test.ts.snap │ │ └── transform.test.ts.snap │ ├── __utils__ │ │ └── linaria-snapshot-serializer.ts │ ├── babel.test.ts │ └── transform.test.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── react └── package.json ├── tsconfig.eslint.json ├── tsconfig.json ├── tsconfig.prod.json ├── tslint.json ├── turbo.json └── website ├── .eslintrc ├── CHANGELOG.md ├── assets ├── callstack-logo.svg ├── code-sample-v4.png ├── code-sample.png ├── linaria-logo.svg ├── linaria-logo@2x.png ├── linaria-logomark.svg └── serverscom-logo-black.svg ├── babel.config.js ├── index.html ├── linaria.config.js ├── package.json ├── serve.config.js ├── src ├── .eslintrc ├── api │ ├── index.js │ └── react │ │ └── index.js ├── components │ ├── App.jsx │ ├── Container.js │ ├── Example.js │ ├── Header.jsx │ └── Hero.jsx ├── index.jsx ├── server.jsx └── styles │ ├── constants.js │ └── utils.js ├── stylelint.config.js └── webpack.config.js /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "master", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | precision: 2 3 | round: down 4 | range: 70...100 5 | 6 | status: 7 | project: true 8 | patch: true 9 | changes: false 10 | 11 | comment: false 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # we recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | __fixtures__/ 2 | coverage/ 3 | lib/ 4 | esm/ 5 | packages/*/bin/ 6 | packages/*/types/ 7 | dist/ 8 | build/ 9 | node_modules/ 10 | vendor/ 11 | examples/ 12 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/linaria/c3721288082a9c568e99d31a3f0bd741e24524ac/.git-blame-ignore-revs -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '🐛 Report a bug' 3 | about: 'Report a reproducible bug or reproducible regression.' 4 | labels: 'bug report 🦗, needs: triage 🏷, needs: complete repro 🖥️' 5 | 6 | --- 7 | 8 | ## Environment 9 | 10 | 13 | 14 | 17 | 18 | - Linaria version: 19 | - Bundler (+ version): 20 | - Node.js version: 21 | - OS: 22 | 23 | ## Description 24 | 25 | 30 | 31 | ## Reproducible Demo 32 | 33 | 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 🤔 Questions and Help 4 | url: https://discord.gg/fAbHe67 5 | about: Reach the community on discord if you need help or have a question. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '💬 Enhancement proposal' 3 | about: Suggest an possible improvement of existing features. 4 | labels: 'enhancement: proposal 💬' 5 | --- 6 | 7 | ## Describe the enhancement 8 | 9 | 10 | 11 | ## Motivation 12 | 13 | 14 | 15 | ## Possible implementations 16 | 17 | 18 | 19 | ## Related Issues 20 | 21 | 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '💬 Feature proposal' 3 | about: Suggest introduction of a new feature. 4 | labels: 'feature: proposal 💬' 5 | --- 6 | 7 | ## Describe the feature 8 | 9 | 10 | 11 | ## Motivation 12 | 13 | 14 | 15 | ## Possible implementations 16 | 17 | 18 | 19 | ## Related Issues 20 | 21 | 22 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ## Motivation 8 | 9 | 16 | 17 | ## Summary 18 | 19 | 24 | 25 | ## Test plan 26 | 27 | 32 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ${{ matrix.os }} 13 | 14 | env: 15 | TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} 16 | TURBO_TEAM: ${{ secrets.TURBO_TEAM }} 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [ubuntu-latest] 22 | node-version: [18.x, 20.x, 22.x] 23 | include: 24 | - os: windows-latest 25 | node-version: 20.x 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | 30 | - uses: pnpm/action-setup@v3 31 | with: 32 | version: 9 33 | run_install: false 34 | 35 | - name: Get pnpm store directory 36 | id: pnpm-cache 37 | shell: bash 38 | run: | 39 | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT 40 | 41 | - name: Setup pnpm cache 42 | uses: actions/cache@v4 43 | with: 44 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} 45 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('./pnpm-lock.yaml') }} 46 | restore-keys: | 47 | ${{ runner.os }}-pnpm-store- 48 | 49 | - name: Use Node.js ${{ matrix.node-version }} 50 | uses: actions/setup-node@v4 51 | with: 52 | node-version: ${{ matrix.node-version }} 53 | 54 | - name: Install and prepare 55 | run: pnpm install --frozen-lockfile --strict-peer-dependencies 56 | - name: ESLint 57 | if: matrix.os == 'ubuntu-latest' && matrix.node-version == '20.x' 58 | run: pnpm lint 59 | - name: TSLint 60 | if: matrix.os == 'ubuntu-latest' && matrix.node-version == '20.x' 61 | run: pnpm turbo run test:dts 62 | - name: Tests 63 | run: pnpm turbo run test 64 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: labeler 2 | on: 3 | issues: 4 | types: [opened, edited, reopened] 5 | pull_request: 6 | types: [opened, edited, reopened, ready_for_review, synchronize] 7 | 8 | jobs: 9 | labeler: 10 | runs-on: ubuntu-latest 11 | name: label issues and pull requests 12 | steps: 13 | - name: check-out-repository 14 | uses: actions/checkout@v4 15 | 16 | - name: labeler 17 | uses: jayu/super-labeler-action@develop 18 | env: 19 | ACTIONS_STEP_DEBUG: ${{ secrets.ACTIONS_STEP_DEBUG }} 20 | with: 21 | github-token: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | concurrency: ${{ github.workflow }}-${{ github.ref }} 9 | 10 | jobs: 11 | release: 12 | name: Release 13 | runs-on: ubuntu-latest 14 | 15 | env: 16 | TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} 17 | TURBO_TEAM: ${{ secrets.TURBO_TEAM }} 18 | 19 | steps: 20 | - name: Checkout Repo 21 | uses: actions/checkout@v4 22 | 23 | - uses: pnpm/action-setup@v3 24 | with: 25 | version: 9 26 | run_install: false 27 | 28 | - name: Get pnpm store directory 29 | id: pnpm-cache 30 | shell: bash 31 | run: | 32 | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT 33 | 34 | - name: Setup pnpm cache 35 | uses: actions/cache@v4 36 | with: 37 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} 38 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('./pnpm-lock.yaml') }} 39 | restore-keys: | 40 | ${{ runner.os }}-pnpm-store- 41 | 42 | - name: Setup Node.js 20.x 43 | uses: actions/setup-node@v4 44 | with: 45 | node-version: 20.x 46 | 47 | - name: Install and prepare 48 | run: pnpm install --frozen-lockfile --strict-peer-dependencies 49 | 50 | - name: Create Release Pull Request or Publish to npm 51 | id: changesets 52 | uses: changesets/action@v1 53 | with: 54 | publish: pnpm run release 55 | env: 56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 57 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 58 | -------------------------------------------------------------------------------- /.github/workflows/site-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | build-and-deploy: 8 | runs-on: ubuntu-latest 9 | 10 | env: 11 | TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} 12 | TURBO_TEAM: ${{ secrets.TURBO_TEAM }} 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - uses: pnpm/action-setup@v3 18 | with: 19 | version: 9 20 | run_install: false 21 | 22 | - name: Get pnpm store directory 23 | id: pnpm-cache 24 | shell: bash 25 | run: | 26 | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT 27 | 28 | - name: Setup pnpm cache 29 | uses: actions/cache@v4 30 | with: 31 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} 32 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('./pnpm-lock.yaml') }} 33 | restore-keys: | 34 | ${{ runner.os }}-pnpm-store- 35 | 36 | - name: Use Node.js 20.x 37 | uses: actions/setup-node@v4 38 | with: 39 | node-version: 20.x 40 | 41 | - name: Install and prepare 42 | run: pnpm install --frozen-lockfile --strict-peer-dependencies 43 | 44 | - name: Deploy 🚀 45 | uses: JamesIves/github-pages-deploy-action@3.7.1 46 | with: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | BRANCH: gh-pages 49 | FOLDER: website 50 | CLEAN: true 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### OS Junk ### 2 | .DS_Store 3 | *~ 4 | .history 5 | 6 | ### Node ### 7 | # Logs 8 | logs 9 | *.log 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (http://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Turborepo cache directory 49 | .turbo 50 | 51 | # Optional eslint cache 52 | .eslintcache 53 | 54 | # Optional REPL history 55 | .node_repl_history 56 | 57 | # Output of 'npm pack' 58 | *.tgz 59 | 60 | # Yarn Integrity file 61 | .yarn-integrity 62 | 63 | # dotenv environment variables file 64 | .env 65 | 66 | # generated files 67 | lib/ 68 | esm/ 69 | types/ 70 | dist/ 71 | build/ 72 | tsconfig.tsbuildinfo 73 | 74 | .linaria-cache 75 | .wyw-cache 76 | 77 | # debug 78 | *.debug.ts 79 | **/linaria-debug 80 | **/wyw-debug 81 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | lockfile = true 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "auto", 3 | "trailingComma": "es5", 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /.syncpackrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | dependencyTypes: ['dev', 'overrides', 'pnpmOverrides', 'prod', 'resolutions'], 3 | filter: '.', 4 | indent: ' ', 5 | semverGroups: [], 6 | semverRange: '', 7 | sortAz: [ 8 | 'contributors', 9 | 'dependencies', 10 | 'devDependencies', 11 | 'keywords', 12 | 'peerDependencies', 13 | 'resolutions', 14 | 'scripts', 15 | ], 16 | // https://github.com/keithamus/sort-package-json/blob/master/defaultRules.md 17 | sortFirst: [ 18 | '$schema', 19 | 'name', 20 | 'displayName', 21 | 'version', 22 | 'private', 23 | 'description', 24 | 'categories', 25 | 'keywords', 26 | 'homepage', 27 | 'bugs', 28 | 'repository', 29 | 'funding', 30 | 'license', 31 | 'qna', 32 | 'author', 33 | 'maintainers', 34 | 'contributors', 35 | 'publisher', 36 | 'sideEffects', 37 | 'type', 38 | 'imports', 39 | 'exports', 40 | 'main', 41 | 'svelte', 42 | 'umd:main', 43 | 'jsdelivr', 44 | 'unpkg', 45 | 'module', 46 | 'source', 47 | 'jsnext:main', 48 | 'browser', 49 | 'react-native', 50 | 'types', 51 | 'typesVersions', 52 | 'typings', 53 | 'style', 54 | 'example', 55 | 'examplestyle', 56 | 'assets', 57 | 'bin', 58 | 'man', 59 | 'directories', 60 | 'files', 61 | 'workspaces', 62 | 'binary', 63 | 64 | 'wyw-in-js', 65 | 66 | 'scripts', 67 | 'betterScripts', 68 | 'contributes', 69 | 'activationEvents', 70 | 'husky', 71 | 'simple-git-hooks', 72 | 'pre-commit', 73 | 'commitlint', 74 | 'lint-staged', 75 | 'config', 76 | 'nodemonConfig', 77 | 'browserify', 78 | 'babel', 79 | 'browserslist', 80 | 'xo', 81 | 'prettier', 82 | 'eslintConfig', 83 | 'eslintIgnore', 84 | 'npmpackagejsonlint', 85 | 'release', 86 | 'remarkConfig', 87 | 'stylelint', 88 | 'ava', 89 | 'jest', 90 | 'mocha', 91 | 'nyc', 92 | 'tap', 93 | 'resolutions', 94 | 'dependencies', 95 | 'devDependencies', 96 | 'dependenciesMeta', 97 | 'peerDependencies', 98 | 'peerDependenciesMeta', 99 | 'optionalDependencies', 100 | 'bundledDependencies', 101 | 'bundleDependencies', 102 | 'extensionPack', 103 | 'extensionDependencies', 104 | 'flat', 105 | 'packageManager', 106 | 'engines', 107 | 'engineStrict', 108 | 'volta', 109 | 'languageName', 110 | 'os', 111 | 'cpu', 112 | 'preferGlobal', 113 | 'publishConfig', 114 | 'icon', 115 | 'badges', 116 | 'galleryBanner', 117 | 'preview', 118 | 'markdown', 119 | 120 | 'tsup', 121 | ], 122 | source: [], 123 | versionGroups: [ 124 | { 125 | dependencies: [ 126 | '@types/enhanced-resolve', 127 | 'css-loader', 128 | 'enhanced-resolve', 129 | 'mini-css-extract-plugin', 130 | 'webpack', 131 | ], 132 | packages: ['@linaria/webpack4-loader', 'webpack4-example'], 133 | }, 134 | ], 135 | workspace: true, 136 | }; 137 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "website/node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [oss@callstack.io](mailto:oss@callstack.io). All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct/ 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Callstack 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * The following environments are supported: 3 | * 4 | * (unspecified): Produces ES module output, no language features 5 | * (except non-standard ones) are transpiled. 6 | * "legacy": Produces CommonJS output, uses @babel/preset-env to target 7 | * Node.js 12 and specific browsers. 8 | * "test": Used by Jest, produces CommonJS output, targetting 9 | * the same version as legacy. 10 | */ 11 | 12 | /* 13 | * Configuration for the legacy build 14 | */ 15 | 16 | const commonJSTargets = { 17 | browsers: '> 0.25% and supports array-includes', 18 | // browsers: 'chrome > 90', 19 | node: '12', 20 | }; 21 | 22 | module.exports = { 23 | presets: ['@babel/preset-typescript'], 24 | plugins: ['@babel/plugin-proposal-explicit-resource-management'], 25 | env: { 26 | legacy: { 27 | presets: [ 28 | [ 29 | '@babel/preset-env', 30 | { 31 | targets: { 32 | node: commonJSTargets.node, 33 | }, 34 | }, 35 | ], 36 | ], 37 | }, 38 | test: { 39 | presets: [ 40 | [ 41 | '@babel/preset-env', 42 | { 43 | targets: { 44 | node: commonJSTargets.node, 45 | }, 46 | }, 47 | ], 48 | '@babel/preset-typescript', 49 | ], 50 | }, 51 | }, 52 | overrides: [ 53 | { 54 | /** 55 | * only react and core packages are targeted to be run in the browser 56 | */ 57 | test: /[\\/]packages[\\/](?:atomic|core|react)[\\/](?!src[\\/]processors[\\/])/, 58 | presets: ['@babel/preset-react'], 59 | env: { 60 | legacy: { 61 | presets: [ 62 | [ 63 | '@babel/preset-env', 64 | { 65 | targets: commonJSTargets.browsers, 66 | loose: true, 67 | // our styled component should not need to use any polyfill. We do not include core-js in dependencies. However, we leave this to detect if future changes would not introduce any need for polyfill 68 | useBuiltIns: 'usage', 69 | // Even core-js doesn't remember IE11 70 | exclude: ['es.array.includes', 'web.dom-collections.iterator'], 71 | corejs: 3, 72 | // this is used to test if we do not introduced core-js polyfill 73 | debug: process.env.DEBUG_CORE_JS === 'true', 74 | }, 75 | ], 76 | ], 77 | }, 78 | }, 79 | }, 80 | { 81 | /** 82 | * we have to transpile JSX in tests 83 | */ 84 | test: /[\\/](__tests__|__fixtures__|packages[\\/]teskit[\\/]src)[\\/]/, 85 | presets: ['@babel/preset-react'], 86 | }, 87 | ], 88 | }; 89 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/CLI.md: -------------------------------------------------------------------------------- 1 | # CLI 2 | 3 | Linaria CLI (`@linaria/cli`) allows you to extract CSS from your source files using a command line. 4 | 5 | ### Usage 6 | 7 | ```bash 8 | yarn linaria [options] [...] 9 | ``` 10 | 11 | Option `-o, --out-dir ` __is always required__. 12 | 13 | You can also use glob for specifying files to process: 14 | 15 | ```bash 16 | yarn linaria -o styles src/component/**/*.js 17 | # or multiple globs 18 | yarn linaria -o styles src/component/**/*.js src/screens/**/*.js 19 | ``` 20 | 21 | CLI supports adding a require statement for generated CSS file automatically: 22 | 23 | ```bash 24 | yarn linaria -o out-dir --source-root src --insert-css-requires dist src/**/*.js 25 | ``` 26 | 27 | where `source-root` is directory with source JS files and `insert-css-requires` has directory with transpiled/compiled JS files. 28 | 29 | ### Options 30 | 31 | * `-o, --out-dir ` (__required__) - Output directory for the extracted CSS files 32 | * `-s, --source-maps` - Generate source maps for the CSS files 33 | * `-r, --source-root ` - Directory containing the source JS files 34 | * `-i, --insert-css-requires ` - Directory containing JS files to insert require statements for the CSS files (__works only if `-r, --source-root` is provided__) 35 | * `-c, --config-file ` - Path to the configuration file. If a relative path is given, it'll be resolved relative to the current working directory. 36 | * `-x, --ignore ""` - Pattern of files to ignore. Be sure to wrap with quotes. 37 | * `-m, --modules ` - Specifies a type of used imports. 38 | * `-t, --transform ` - Replace template tags with evaluated values. 39 | 40 | -------------------------------------------------------------------------------- /docs/CRITICAL_CSS.md: -------------------------------------------------------------------------------- 1 | # Critical CSS extraction 2 | 3 | Since Linaria extracts the CSS statically at build time, you don't need to setup a server rendering. Usually, critical CSS extraction will be automatic if you are code splitting your code and using something like [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) for webpack to generate your CSS files. 4 | 5 | If you're not code splitting, or the initial CSS chunk is not representative of initially rendered content, you might want to extract critical CSS using the `collect` helper we provide to ship the minimal amount of CSS used in the page to the browser. To be able to use the `collect` helper, you need to provide the initial HTML, which usually means that you need to have SSR setup for your web app. 6 | 7 | The `collect` method takes some HTML and CSS and gives you the critical CSS: 8 | 9 | ```js 10 | import { collect } from '@linaria/server'; 11 | 12 | const { critical, other } = collect(html, css); 13 | ``` 14 | 15 | For example, in an express app with React, you could do something like the following: 16 | 17 | ```js 18 | import fs from 'fs'; 19 | import express from 'express'; 20 | import crypto from 'crypto'; 21 | import React from 'react'; 22 | import ReactDOMServer from 'react-dom/server'; 23 | import { collect } from '@linaria/server'; 24 | import App from './App'; 25 | 26 | const cache = {}; 27 | const css = fs.readFileSync('./dist/styles.css', 'utf8'); 28 | const app = express(); 29 | 30 | app.get('/', (req, res) => { 31 | const html = ReactDOMServer.renderToString(); 32 | const { critical, other } = collect(html, css); 33 | const slug = crypto.createHash('md5').update(other).digest('hex'); 34 | 35 | cache[slug] = other; 36 | 37 | res.end(` 38 | 39 | 40 | App 41 | 42 | 43 | 44 |
45 | ${html} 46 |
47 | 48 | 49 | 50 | `); 51 | }); 52 | 53 | app.get('/styles/:slug', (req, res) => { 54 | res.type('text/css'); 55 | res.end(cache[req.params.slug]) 56 | }); 57 | 58 | app.listen(3242); 59 | ``` 60 | 61 | By placing the non-critical CSS at the end of `body`, you can make sure that page rendering is not blocked until the CSS is loaded. You can also load the non-critical CSS lazily with JavaScript once the page has loaded for a more efficient strategy. However, it's highly recommended that you take advantage of code splitting in webpack which gives you automatic CSS chunks in addition to critical CSS. 62 | -------------------------------------------------------------------------------- /docs/DYNAMIC_STYLES.md: -------------------------------------------------------------------------------- 1 | # Dynamic styles with `css` tag 2 | 3 | Sometimes we have some styles based on component's props or state, or dynamic in some way. If you use the `styled` helper with React, this is automatically handled using CSS custom properties. But we cannot do the same for `css` tags since they aren't linked to any component, and so we don't have access to state and props. However, there are some approaches to tackle this, each with their own limitations. 4 | 5 | ## Inline styles 6 | 7 | Inline styles are the most straightforward way to use dynamic styles. Pass a `style` object with the dynamic styles, and you're done. 8 | 9 | ```js 10 | import React from 'react'; 11 | 12 | export function Pager({ index, children }) { 13 | return ( 14 |
15 | {children} 16 |
17 | ); 18 | } 19 | ``` 20 | 21 | However, it's not possible to use inline styles with pseudo-selectors or media queries. 22 | 23 | ## CSS custom properties 24 | 25 | [CSS custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/--*) can be used to expose dynamic properties to the CSS. 26 | 27 | ```js 28 | import React from 'react'; 29 | import { css } from '@linaria/core'; 30 | 31 | const box = css` 32 | height: var(--box-size); 33 | width: var(--box-size); 34 | `; 35 | 36 | export function Box({ size }) { 37 | return ( 38 |
42 | ); 43 | } 44 | ``` 45 | 46 | The [browser support for CSS custom properties](http://caniuse.com/#feat=css-variables) is limited, and it's not polyfilled. Therefore it's not a viable approach if you need to support older browsers. Worth noting that custom properties cascade, so if you don't override the value for the current element, and a custom property with the same name exists for a parent element, it'll be used instead. 47 | 48 | ## Data attributes 49 | 50 | In cases where you know the values ahead of time, you can use [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes) to dynamically switch the styles that are applied. 51 | 52 | ```js 53 | import React from 'react'; 54 | import { css } from '@linaria/core'; 55 | 56 | const box = css` 57 | &[data-valid] { 58 | color: yellow; 59 | } 60 | &[data-valid="invalid"] { 61 | color: red; 62 | } 63 | &[data-valid="valid"] { 64 | color: green; 65 | } 66 | ` 67 | 68 | export function Box({ color, valid }) { 69 | return ( 70 |
74 | ); 75 | } 76 | ``` 77 | 78 | ## `currentColor` 79 | 80 | For color values, browsers support a `currentColor` property which points to the text color of the element. It is well supported in all browsers. 81 | 82 | ```js 83 | import React from 'react'; 84 | import { css } from '@linaria/core'; 85 | 86 | const box = css` 87 | background-color: currentColor; 88 | `; 89 | 90 | const content = css` 91 | color: white; 92 | `; 93 | 94 | export function Box({ color }) { 95 | return ( 96 |
100 | 101 | ¯\_(ツ)_/¯ 102 | 103 |
104 | ); 105 | } 106 | ``` 107 | 108 | You cannot use this approach if the dynamic value is not a color, or the element contains some text which needs to be styled with a different color. If the element has children, you will need to reset the `color` property for the text. 109 | -------------------------------------------------------------------------------- /docs/LINTING.md: -------------------------------------------------------------------------------- 1 | # Linting 2 | 3 | There are separate installations based on whether you use stylelint v13 or stylelint v14 4 | 5 | ## Stylelint 13 6 | 7 | For linting styles with [stylelint 13](https://stylelint.io/), use `@linaria/stylelint`. 8 | 9 | ### Installation 10 | 11 | Install `stylelint` and optionally your favorite config (such as `stylelint-config-recommended`) in your project: 12 | 13 | ```bash 14 | yarn add --dev stylelint stylelint-config-recommended @linaria/stylelint 15 | ``` 16 | 17 | ### Configuring stylelint 18 | 19 | All you need to do is to set your config to extend from `@linaria/stylelint`. 20 | 21 | Here's the example `.stylelintrc` configuration file: 22 | 23 | ```json 24 | { 25 | "extends": [ 26 | "stylelint-config-recommended", 27 | "@linaria/stylelint" 28 | ] 29 | } 30 | ``` 31 | 32 | Please refer to the [official stylelint documentation](https://stylelint.io/user-guide/configuration/) for more info about configuration. 33 | 34 | The preprocessor will use the [options from the configuration file](/docs/CONFIGURATION.md) for processing your files. 35 | 36 | ## Stylelint 14 37 | 38 | For linting styles with [stylelint 14](https://stylelint.io/), use `@linaria/stylelint-config-standard-linaria`. 39 | 40 | ### Installation 41 | 42 | Install `stylelint` and `@linaria/stylelint-config-standard-linaria` 43 | 44 | ```bash 45 | yarn add --dev stylelint @linaria/stylelint-config-standard-linaria 46 | ``` 47 | 48 | ### Configuring stylelint 49 | 50 | For the standard configuration you can extend from `@linaria/stylelint-config-standard-linaria`. 51 | 52 | Here's an example `.stylelintrc` configuration file: 53 | 54 | ```json 55 | { 56 | "extends": [ 57 | "@linaria/stylelint-config-standard-linaria" 58 | ] 59 | } 60 | ``` 61 | 62 | `@linaria/stylelint-config-standard-linaria` extends `stylelint-config-standard` which extends `stylelint-config-recommended` so you do NOT need to add those separately. It also sets the customSyntax as `@linaria/postcss-linaria` and adds a few rules. 63 | 64 | Alternatively, to just use the custom syntax you can add `@linaria/postcss-linaria` 65 | Here's an example `.stylelintrc` configuration file: 66 | 67 | ```json 68 | { 69 | "customSyntax": "@linaria/postcss-linaria" 70 | } 71 | ``` 72 | 73 | Please refer to the [official stylelint documentation](https://stylelint.io/user-guide/configuration/) for more info about configuration. 74 | 75 | ### Why did the configuration change between Stylelint v13 and v14? 76 | 77 | Stylelint 14 encourages the use of a [custom syntax](https://stylelint.io/developer-guide/syntaxes/) instead of a processor. `@linaria/stylelint-config-standard-linaria` sets the custom syntax to `@linaria/postcss-linaria`, a custom syntax for linaria, whereas @linaria/stylelint uses a processor. The custom syntax has the benefit of being able to support `stylelint --fix` whereas the processor cannot. 78 | 79 | ## Usage 80 | 81 | ### Linting your files 82 | 83 | Add the following to your `package.json` scripts: 84 | 85 | ```json 86 | "lint:css": "stylelint src/**/*.js" 87 | ``` 88 | 89 | Now, you can run `yarn lint:css` to lint the CSS in your JS files with stylelint. 90 | 91 | For more information refer to [stylelint documentation](https://stylelint.io/user-guide/cli/). 92 | 93 | ## Editor Setup 94 | 95 | In order to make the 96 | [vscode-stylelint](https://github.com/stylelint/vscode-stylelint) 97 | extension work with this syntax correctly, you must configure it 98 | to validate the files you use linaria in by specifying an array of [language 99 | identifiers](https://code.visualstudio.com/docs/languages/overview#_changing-the-language-for-the-selected-file). 100 | 101 | You can do this by following these 102 | [instructions](https://github.com/stylelint/vscode-stylelint#stylelintvalidate). 103 | 104 | For example: 105 | 106 | ```json 107 | { 108 | "stylelint.validate": ["typescriptreact"] 109 | } 110 | ``` 111 | 112 | -------------------------------------------------------------------------------- /docs/MIGRATION_GUIDE.md: -------------------------------------------------------------------------------- 1 | # Migration Guide 2 | 3 | # 6.x from 5.x, 4.x, 3.x 4 | 5 | ## For Users 6 | 7 | The main breaking change is that all tooling has been moved from the `@linaria` scope to the `@wyw-in-js` scope. This means that you will need to update your dependencies as follows: 8 | 9 | | Old | New 10 | | --- | --- 11 | |@linaria/babel-preset | @wyw-in-js/babel-preset 12 | |@linaria/cli | @wyw-in-js/cli 13 | |@linaria/esbuild | @wyw-in-js/esbuild 14 | |@linaria/rollup | @wyw-in-js/rollup 15 | |@linaria/shaker | discontinued 16 | |@linaria/vite | @wyw-in-js/vite 17 | |@linaria/webpack4-loader | discontinued 18 | |@linaria/webpack5-loader | @wyw-in-js/webpack-loader 19 | 20 | There is no longer a need to install `@linaria/shaker` as it is now part of `@wyw-in-js/transform`, which will be installed automatically with the bundler plugins. 21 | 22 | The configuration file has been renamed from `linaria.config.js` (`linariarc`) to `wyw-in-js.config.js` (`.wyw-in-jsrc`). 23 | 24 | ## For Custom Processor Developers 25 | 26 | Base classes for processors and most helpers have been moved to `@wyw-in-js/processor-utils`. 27 | 28 | All APIs that had `linaria` in their names have been renamed: 29 | 30 | - The field that stores meta information in runtime has been renamed from `__linaria` to `__wyw_meta` 31 | - The export with all interpolated values has been renamed from `__linariaPreval` to `__wywPreval` 32 | - The caller name in Babel has been renamed from `linaria` to `wyw-in-js` 33 | 34 | For additional information, please visit the [wyw-in-js.dev](https://wyw-in-js.dev). 35 | 36 | # 4.x, 3.x from 2.x 37 | 38 | This release was mostly a refactor to [split into more packages](https://github.com/callstack/linaria/pull/687/). 39 | 40 | ## Breaking changes 41 | 42 | All these package imports in code need to be updated: 43 | 44 | | Old | New 45 | | --- | --- 46 | |linaria | @linaria/core 47 | |linaria/loader | @linaria/webpack4-loader, @linaria/webpack5-loader 48 | |linaria/react | @linaria/react 49 | |linaria/rollup | @linaria/rollup 50 | |linaria/server | @linaria/server 51 | |linaria/stylelint-config | @linaria/stylelint 52 | 53 | 54 | The `shaker` evaluator has moved from `linaria/evaluators` into its own package. You'll need to add `@linaria/shaker` to your package.json even if you never import it. 55 | 56 | The Babel preset moved from `linaria/babel` to `@linaria/babel-preset` but has to be referenced as `@linaria` in a Babel config. See https://github.com/callstack/linaria/issues/704 57 | 58 | 59 | In package.json import all the new packages you use. 60 | 61 | # 2.x from 1.x 62 | 63 | ## Breaking changes 64 | 65 | ### `Core-js` dependency removal and _theoretical_ drop compatibility for `node` below `10` 66 | 67 | In [#569](https://github.com/callstack/linaria/pull/569) We removed `core-js` dependency. 68 | 69 | It should not effectively affect your users or build pipelines. But it was technically a breaking change. 70 | 71 | We set babel preset that makes all non-browser dependencies compatible with `node` from version `10`. But previous setup was using `browser` env so If you was able to build Linaria with previous versions of node, it should work also now. Support for browsers environment didn't change. 72 | 73 | After that you should be able to solve issues with `core-js` dependency in your project, because it will no longer collide with version used by Linaria. 74 | 75 | ### The default [evaluation strategy](./HOW_IT_WORKS.md#evaluators) has been changed to `shaker` 76 | 77 | It should not affect existed code since the new strategy is more powerful, but you can always switch to the old one by adding the next `rules` section to your Linaria-config: 78 | ```js 79 | [ 80 | { 81 | action: require('linaria/evaluators').extractor, 82 | }, 83 | { 84 | test: /\/node_modules\//, 85 | action: 'ignore', 86 | }, 87 | ] 88 | ``` 89 | -------------------------------------------------------------------------------- /docs/THEMING.md: -------------------------------------------------------------------------------- 1 | # Theming 2 | 3 | There are several approaches you can use for theming. Depending on the browser support and requirements, you can pick the approach that suits you the best. 4 | 5 | ## CSS custom properties 6 | 7 | CSS custom properties aka CSS variables are one of the best ways to apply a theme to your web app. The basic concept is that we add a class name to represent the theme to our root element, and use different values for our CSS variables based on the theme: 8 | 9 | ```js 10 | // Create class names for different themes 11 | const a = css` 12 | --color-primary: #6200ee; 13 | --color-accent: #03dac4; 14 | `; 15 | 16 | const b = css` 17 | --color-primary: #03a9f4; 18 | --color-accent: #e91e63; 19 | `; 20 | 21 | // Apply a theme to the root element 22 | ; 23 | ``` 24 | 25 | Now, we can use these variables in any of the child elements: 26 | 27 | ```js 28 | const Button = styled.button` 29 | background-color: var(--color-accent); 30 | `; 31 | ``` 32 | 33 | CSS custom properties are [not supported in some browsers such as IE](http://caniuse.com/#feat=css-variables), so if you need to support those browsers, this is not a viable approach. 34 | 35 | ## Class names 36 | 37 | Another approach is to add a class name representing the theme (e.g. - `theme-dark`) in the root element, and take advantage of CSS child selectors to theme the elements based on this parent class name. 38 | 39 | For example, let's add the theme to the root component: 40 | 41 | ```js 42 | 43 | ``` 44 | 45 | Now, we can conditionally style any child element according to the theme: 46 | 47 | ```js 48 | const Header = styled.h1` 49 | text-transform: uppercase; 50 | 51 | .theme-dark & { 52 | color: white; 53 | } 54 | 55 | .theme-light & { 56 | color: black; 57 | } 58 | `; 59 | ``` 60 | 61 | You could even make some helpers to make writing this easier: 62 | 63 | ```js 64 | // Put your colors in an object grouped by the theme names 65 | const colors = { 66 | light: { 67 | text: 'black', 68 | }, 69 | dark: { 70 | text: 'white', 71 | }, 72 | }; 73 | 74 | // Create a small helper function to loop over the themes and create CSS rule sets 75 | const theming = cb => 76 | Object.keys(colors).reduce((acc, name) => Object.assign(acc, { 77 | [`.theme-${name} &`]: cb(colors[name]), 78 | }), {}); 79 | 80 | // Use the helper in your styles 81 | const Header = styled.h1` 82 | text-transform: uppercase; 83 | 84 | ${theming(c => ({ 85 | color: c.text, 86 | }))}; 87 | `; 88 | ``` 89 | 90 | This approach works in all browsers, and is the best approach if you want to support older browsers without support for CSS custom properties. 91 | 92 | ## React Context 93 | 94 | Another approach is to use React Context to pass down colors, and then use function interpolations with the `styled` tag to use the colors in your component. You could use something like [`@callstack/react-theme-provider`](https://github.com/callstack/react-theme-provider) or write your own HOC. Then use it like: 95 | 96 | ```js 97 | const Button = withTheme(styled.button` 98 | background-color: ${props => props.theme.accent}; 99 | `); 100 | ``` 101 | 102 | Note that this approach also uses CSS custom properties under the hood since function interpolations compile down to CSS custom properties. So the browser support is limited. 103 | -------------------------------------------------------------------------------- /examples/astro-solid/astro.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | import astro_solid from '@astrojs/solid-js'; 3 | import vite_wyw from '@wyw-in-js/vite'; 4 | import vite_inspect from 'vite-plugin-inspect'; 5 | 6 | export default defineConfig({ 7 | output: 'static', 8 | srcDir: '.', 9 | root: '.', 10 | integrations: [astro_solid()], 11 | server: { 12 | host: '0.0.0.0', 13 | port: 3000, 14 | }, 15 | build: { 16 | format: 'file', 17 | }, 18 | vite: { 19 | plugins: [ 20 | vite_wyw({ 21 | displayName: true, 22 | classNameSlug: (hash, title, args) => `${args.dir}_${title}_${hash}`, 23 | babelOptions: { 24 | presets: ['solid'], 25 | }, 26 | }), 27 | vite_inspect(), 28 | ], 29 | css: { 30 | modules: false, 31 | }, 32 | }, 33 | }); 34 | -------------------------------------------------------------------------------- /examples/astro-solid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astro-solid-example", 3 | "version": "0.0.1", 4 | "license": "MIT", 5 | "main": "index.js", 6 | "type": "module", 7 | "devDependencies": { 8 | "@astrojs/solid-js": "^1.2.3", 9 | "@babel/core": "^7.23.5", 10 | "@linaria/core": "workspace:^", 11 | "@wyw-in-js/vite": "^0.6.0", 12 | "astro": "^1.6.10", 13 | "solid-js": "^1.6.2", 14 | "vite": "^3", 15 | "vite-plugin-inspect": "^0.7.33" 16 | }, 17 | "scripts": { 18 | "dev": "astro dev --force", 19 | "build": "astro build" 20 | }, 21 | "author": "Dmitriy Nikiforov" 22 | } 23 | -------------------------------------------------------------------------------- /examples/astro-solid/pages/csr.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@linaria/core'; 2 | import { CSRChild, cssVariableFromModule } from './csr_child'; 3 | import { astroTextColor } from './external'; 4 | 5 | // Try to change some variables white in dev mode 6 | const GLOBAL_VARS = { 7 | color_header: 'red', 8 | } as const; 9 | 10 | export default function CSRComponent() { 11 | const LOCAL_VARS = { 12 | description_font_style: 'bold', 13 | } as const; 14 | return ( 15 |
16 |

17 | Hello! This is{' '} 18 | 23 | Solid 24 | {' '} 25 | app built with{' '} 26 | 31 | Linaria 32 | {' '} 33 | powered by 34 |
35 | 41 | Astro 42 | 43 |

44 |
49 | This component was rendered on client. 50 |
51 | 52 |
53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /examples/astro-solid/pages/csr_child.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@linaria/core'; 2 | import { astroTextColor } from './external'; 3 | 4 | export function CSRChild() { 5 | return ( 6 |
12 | I am child module! 13 |
14 | ); 15 | } 16 | // Try to change some variables white in dev mode 17 | export const cssVariableFromModule = '64px'; 18 | -------------------------------------------------------------------------------- /examples/astro-solid/pages/external.ts: -------------------------------------------------------------------------------- 1 | export const astroTextColor = 'orange'; 2 | -------------------------------------------------------------------------------- /examples/astro-solid/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import CSRComponent from './csr'; 3 | --- 4 | 5 | 6 | 7 | 8 | Linaria – zero-runtime CSS in JS library 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/astro-solid/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "module": "ESNext", 5 | "target": "ESNext", 6 | "jsx": "preserve", 7 | "jsxImportSource": "solid-js", 8 | "moduleResolution": "Node" 9 | }, 10 | "exclude": ["node_modules"] 11 | } 12 | -------------------------------------------------------------------------------- /examples/esbuild/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /examples/esbuild/app.js: -------------------------------------------------------------------------------- 1 | import 'linaria-website'; 2 | -------------------------------------------------------------------------------- /examples/esbuild/build.js: -------------------------------------------------------------------------------- 1 | const wyw = require('@wyw-in-js/esbuild').default; 2 | 3 | const prod = process.env.NODE_ENV === 'production'; 4 | 5 | require('esbuild') 6 | .build({ 7 | bundle: true, 8 | entryPoints: ['app.js'], 9 | loader: { 10 | '.svg': 'file', 11 | '.png': 'file', 12 | }, 13 | minify: prod, 14 | outfile: 'build/out.js', 15 | plugins: [ 16 | wyw({ 17 | sourceMap: prod, 18 | }), 19 | ], 20 | }) 21 | .catch(() => process.exit(1)); 22 | -------------------------------------------------------------------------------- /examples/esbuild/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "esbuild-example", 4 | "version": "0.0.0", 5 | "license": "MIT", 6 | "dependencies": { 7 | "linaria-website": "workspace:^" 8 | }, 9 | "devDependencies": { 10 | "@wyw-in-js/esbuild": "^0.6.0", 11 | "esbuild": "^0.15.16" 12 | }, 13 | "scripts": { 14 | "build": "node build.js" 15 | }, 16 | "author": "Anton Evzhakov" 17 | } 18 | -------------------------------------------------------------------------------- /examples/rollup/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /examples/rollup/app.js: -------------------------------------------------------------------------------- 1 | import 'linaria-website'; 2 | -------------------------------------------------------------------------------- /examples/rollup/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@babel/preset-react'], 3 | }; 4 | -------------------------------------------------------------------------------- /examples/rollup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "rollup-example", 4 | "version": "0.0.0", 5 | "license": "MIT", 6 | "dependencies": { 7 | "linaria-website": "workspace:^" 8 | }, 9 | "devDependencies": { 10 | "@babel/core": "^7.23.5", 11 | "@babel/preset-react": "^7.23.3", 12 | "@rollup/plugin-babel": "^6.0.3", 13 | "@rollup/plugin-commonjs": "^25.0.4", 14 | "@rollup/plugin-image": "^3.0.2", 15 | "@rollup/plugin-node-resolve": "^15.2.1", 16 | "@wyw-in-js/rollup": "^0.6.0", 17 | "rollup": "^3.29.5", 18 | "rollup-plugin-css-only": "^4.3.0" 19 | }, 20 | "scripts": { 21 | "build": "rollup -c" 22 | }, 23 | "author": "Anton Evzhakov" 24 | } 25 | -------------------------------------------------------------------------------- /examples/rollup/rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import wyw from '@wyw-in-js/rollup'; 2 | import { babel } from '@rollup/plugin-babel'; 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | import image from '@rollup/plugin-image'; 5 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 6 | import css from 'rollup-plugin-css-only'; 7 | 8 | export default { 9 | input: 'app.js', 10 | output: { 11 | dir: 'build', 12 | format: 'cjs', 13 | }, 14 | plugins: [ 15 | image(), 16 | wyw({ 17 | sourceMap: process.env.NODE_ENV !== 'production', 18 | }), 19 | css({ 20 | output: 'styles.css', 21 | }), 22 | nodeResolve({ 23 | extensions: ['.jsx', '.js'], 24 | }), 25 | commonjs(), 26 | babel({ babelHelpers: 'bundled' }), 27 | ], 28 | }; 29 | -------------------------------------------------------------------------------- /examples/vite/.linariarc.mjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: [ 3 | { 4 | action: require.resolve('@linaria/shaker'), 5 | }, 6 | { 7 | test: /\/node_modules\//, 8 | action: 'ignore', 9 | }, 10 | { 11 | test: (filename, code) => { 12 | if (!/\/node_modules\//.test(filename)) { 13 | return false; 14 | } 15 | 16 | return /(?:^|\*\/|;|})\s*(?:export|import)\s/m.test(code); 17 | }, 18 | action: require.resolve('@linaria/shaker'), 19 | }, 20 | ], 21 | }; 22 | -------------------------------------------------------------------------------- /examples/vite/app.js: -------------------------------------------------------------------------------- 1 | import 'linaria-website'; 2 | -------------------------------------------------------------------------------- /examples/vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Linaria – zero-runtime CSS in JS library 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/vite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "vite-example", 4 | "version": "0.0.0", 5 | "license": "MIT", 6 | "main": "index.js", 7 | "type": "module", 8 | "dependencies": { 9 | "linaria-website": "workspace:^" 10 | }, 11 | "devDependencies": { 12 | "@rollup/plugin-node-resolve": "^15.2.1", 13 | "@vitejs/plugin-react": "^2.1.0", 14 | "@wyw-in-js/vite": "^0.6.0", 15 | "vite": "^3.2.10" 16 | }, 17 | "scripts": { 18 | "build": "vite build" 19 | }, 20 | "author": "Anton Evzhakov" 21 | } 22 | -------------------------------------------------------------------------------- /examples/vite/vite.config.js: -------------------------------------------------------------------------------- 1 | import wyw from '@wyw-in-js/vite'; 2 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 3 | import react from '@vitejs/plugin-react'; 4 | import { defineConfig } from 'vite'; 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig(({ command }) => ({ 8 | plugins: [ 9 | nodeResolve({ 10 | extensions: ['.jsx', '.js'], 11 | }), 12 | wyw({ 13 | include: ['**/*.{js,jsx}'], 14 | babelOptions: { 15 | presets: ['@babel/preset-react'], 16 | }, 17 | }), 18 | react({ 19 | jsxRuntime: 'classic', 20 | }), 21 | ], 22 | build: { 23 | target: command === 'serve' ? 'modules' : 'es2015', 24 | }, 25 | })); 26 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vpssr-linaria-solid-example", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "node ./server", 7 | "build": "vite build", 8 | "preview": "vite build && NODE_ENV=production node ./server" 9 | }, 10 | "author": "mrfoxpro", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@linaria/core": "workspace:^", 14 | "@wyw-in-js/vite": "^0.6.0", 15 | "babel-preset-solid": "^1.6.2", 16 | "compression": "^1.7.4", 17 | "express": "^4.20.0", 18 | "sirv": "^2.0.2", 19 | "solid-js": "^1.6.2", 20 | "vite": "^3.2.10", 21 | "vite-plugin-solid": "^2.4.0", 22 | "vite-plugin-ssr": "^0.4.54" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/pages/html-js/_default.page.client.js: -------------------------------------------------------------------------------- 1 | // We could also define `handleCounter()` in `index.page.client.js` instead of `_default.page.client.js`. 2 | // We define `_default.page.client.js` to showcase how to define the browser-side JavaScript for multiple pages. 3 | 4 | handleCounter() 5 | 6 | function handleCounter() { 7 | const counterEl = document.querySelector('button') 8 | let countState = 0 9 | const txt = () => `Counter ${countState} (Vanilla JS)` 10 | counterEl.textContent = txt() 11 | counterEl.onclick = () => { 12 | countState++ 13 | counterEl.textContent = txt() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/pages/html-js/index.page.server.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@linaria/core' 2 | 3 | export function Page() { 4 | return ( 5 | <> 6 |

HTML + JS

7 |

This page is rendered to HTML and has only few lines of browser-side JavaScript.

8 |

9 | 10 |

11 |

12 | HMR works for CSS: modify pages/html-js/index.css to change the color of this{' '} 13 | 18 | red text 19 | 20 | . 21 |

22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/pages/html-only/index.page.server.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@linaria/core' 2 | 3 | export function Page() { 4 | return ( 5 | <> 6 |

HTML-only

7 |

8 | The React component Page of this page is rendererd to HTML only. 9 |

10 |

This page has zero browser-side JavaScript. (In development, Vite's HMR client is loaded.)

11 |

12 | As shown by this{' '} 13 | 18 | orange text 19 | 20 | , CSS can be loaded in .page.server.js files. 21 |

22 |

23 | If needed, we can add a little bit of browser-side JavaScipt to implement bits of interactivity, 24 | see HTML + JS. 25 |

26 | 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/pages/index.page.server.tsx: -------------------------------------------------------------------------------- 1 | export function Page() { 2 | return ( 3 | <> 4 |

Render Modes

5 |
    6 |
  • 7 | HTML only. Rendered to HTML, zero browser-side JavaScript. 8 |
  • 9 |
  • 10 | SPA. Rendered to the browser's DOM (not rendered to HTML). 11 |
  • 12 |
  • 13 | HTML + JS. Rendered to HTML, some browser-side JavaScript. 14 |
  • 15 |
  • 16 | SSR. Rendered to HTML and hydrated in the browser. 17 |
  • 18 |
19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/pages/spa/index.page.client.tsx: -------------------------------------------------------------------------------- 1 | import { Counter } from '../ssr/Counter' 2 | import { css } from '@linaria/core' 3 | 4 | export function Page() { 5 | return ( 6 | <> 7 |

SPA

8 |

This page is:

9 |
    10 |
  • Rendered only to the browser's DOM. (Not rendered to HTML.)
  • 11 |
  • 12 | Interactive. 13 |
  • 14 |
15 |

20 | Green text. 21 |

22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/pages/ssr/Counter.tsx: -------------------------------------------------------------------------------- 1 | import { createSignal } from 'solid-js' 2 | 3 | export function Counter() { 4 | const [count, setCount] = createSignal(0) 5 | return ( 6 | 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/pages/ssr/index.page.tsx: -------------------------------------------------------------------------------- 1 | import { Counter } from './Counter' 2 | import { css } from '@linaria/core' 3 | 4 | export function Page() { 5 | return ( 6 | <> 7 |

SSR

8 |

This page is:

9 |
    10 |
  • Rendered to HTML and hydrated in the browser.
  • 11 |
  • 12 | Interactive. 13 |
  • 14 |
15 |

20 | Blue text. 21 |

22 | 23 | ) 24 | } 25 | 26 | export const hydrate = true 27 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/renderer/_default.page.client.tsx: -------------------------------------------------------------------------------- 1 | import { render as solidRender, hydrate as solidHydrate } from 'solid-js/web' 2 | import type { PageContextBuiltInClient } from 'vite-plugin-ssr/client' // When using Client Routing 3 | export async function render(pageContext: PageContextBuiltInClient) { 4 | const { Page, exports, isHydration } = pageContext 5 | if ('hydrate' in exports && isHydration) { 6 | console.log('hydrating') 7 | solidHydrate(() => , document.body) 8 | return 9 | } 10 | solidRender(() => , document.body) 11 | } 12 | export const clientRouting = true 13 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/renderer/_default.page.server.tsx: -------------------------------------------------------------------------------- 1 | import { generateHydrationScript, renderToStream } from 'solid-js/web' 2 | import { escapeInject, stampPipe, dangerouslySkipEscape } from 'vite-plugin-ssr' 3 | import type { PageContextBuiltInClient } from 'vite-plugin-ssr/client' // When using Client Routing 4 | 5 | export function render(pageContext: PageContextBuiltInClient) { 6 | if (pageContext.Page) { 7 | const { Page } = pageContext 8 | const { pipe } = renderToStream(() => ) 9 | 10 | stampPipe(pipe, 'node-stream') 11 | 12 | return escapeInject` 13 | 14 | 15 | 16 | 17 | ${dangerouslySkipEscape(generateHydrationScript())} 18 | 19 | ${pipe} 20 | ` 21 | } 22 | 23 | return escapeInject` 24 | 25 | 26 | 27 | 28 | 29 | 30 | ` 31 | } 32 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const compression = require("compression"); 3 | const { renderPage } = require("vite-plugin-ssr"); 4 | 5 | const isProduction = process.env.NODE_ENV === "production"; 6 | 7 | const app = express(); 8 | app.use(compression()); 9 | 10 | async function main () { 11 | if (isProduction) { 12 | const sirv = require("sirv") 13 | app.use(sirv(`${__dirname}/dist/client`)); 14 | } else { 15 | const vite = require("vite"); 16 | const server = await vite.createServer({ 17 | root: __dirname, 18 | configFile: './vite.config.js', 19 | server: { middlewareMode: true }, 20 | }) 21 | app.use(server.middlewares); 22 | } 23 | 24 | app.get("*", async (req, res, next) => { 25 | const pageContextInit = { 26 | urlOriginal: req.originalUrl, 27 | }; 28 | const pageContext = await renderPage(pageContextInit); 29 | const { httpResponse } = pageContext; 30 | if (!httpResponse) return next(); 31 | const { statusCode, contentType, earlyHints } = httpResponse; 32 | if (res.writeEarlyHints) 33 | res.writeEarlyHints({ link: earlyHints.map((e) => e.earlyHintLink) }); 34 | res.status(statusCode).type(contentType); 35 | httpResponse.pipe(res); 36 | }); 37 | 38 | const port = process.env.PORT || 3000; 39 | app.listen(port); 40 | console.log(`Server running at http://localhost:${port}`); 41 | } 42 | main() 43 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | "jsx": "preserve", 7 | "noEmit": true, 8 | "allowSyntheticDefaultImports": true, 9 | "moduleResolution": "node", 10 | // Misc 11 | "esModuleInterop": true, 12 | "types": [ 13 | "vite/client" 14 | ], 15 | "skipDefaultLibCheck": true, 16 | "skipLibCheck": true, 17 | }, 18 | "exclude": ["node_modules"] 19 | } 20 | -------------------------------------------------------------------------------- /examples/vpssr-linaria-solid/vite.config.js: -------------------------------------------------------------------------------- 1 | import vite_ssr from 'vite-plugin-ssr/plugin'; 2 | import vite_wyw from '@wyw-in-js/vite'; 3 | import vite_solid from 'vite-plugin-solid'; 4 | 5 | export default (/** @type import('vite').ConfigEnv */ { mode }) => { 6 | const dev = mode === 'development'; 7 | /** @type import('vite').UserConfig */ 8 | const config = { 9 | plugins: [ 10 | { 11 | ...vite_wyw({ 12 | babelOptions: { 13 | presets: [ 14 | 'solid', 15 | [ 16 | '@babel/preset-typescript', 17 | { 18 | isTSX: true, 19 | allExtensions: true, 20 | }, 21 | ], 22 | ], 23 | }, 24 | }), 25 | enforce: 'pre', 26 | }, 27 | vite_solid({ 28 | dev: dev, 29 | hot: dev, 30 | ssr: true, 31 | }), 32 | vite_ssr({ 33 | includeAssetsImportedByServer: true, 34 | prerender: { 35 | partial: true, 36 | noExtraDir: true, 37 | }, 38 | }), 39 | ], 40 | }; 41 | return config; 42 | }; 43 | -------------------------------------------------------------------------------- /examples/webpack5/app.js: -------------------------------------------------------------------------------- 1 | import 'linaria-website'; 2 | -------------------------------------------------------------------------------- /examples/webpack5/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@babel/preset-react'], 3 | }; 4 | -------------------------------------------------------------------------------- /examples/webpack5/index.html: -------------------------------------------------------------------------------- 1 | ../../website/index.html -------------------------------------------------------------------------------- /examples/webpack5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "webpack5-example", 4 | "version": "0.0.0", 5 | "license": "MIT", 6 | "main": "index.js", 7 | "dependencies": { 8 | "linaria-website": "workspace:^" 9 | }, 10 | "devDependencies": { 11 | "@babel/core": "^7.23.5", 12 | "@wyw-in-js/webpack-loader": "^0.6.0", 13 | "babel-loader": "^9.1.0", 14 | "cross-env": "^7.0.3", 15 | "css-hot-loader": "^1.4.4", 16 | "css-loader": "^6.7.2", 17 | "file-loader": "^6.2.0", 18 | "mini-css-extract-plugin": "^2.7.0", 19 | "webpack": "^5.94.0", 20 | "webpack-cli": "^5.0.0" 21 | }, 22 | "scripts": { 23 | "build": "cross-env NODE_ENV=production webpack", 24 | "preview": "pnpm build && pnpm dlx http-server ." 25 | }, 26 | "author": "Anton Evzhakov" 27 | } 28 | -------------------------------------------------------------------------------- /examples/webpack5/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 4 | 5 | const dev = process.env.NODE_ENV !== 'production'; 6 | 7 | module.exports = { 8 | mode: dev ? 'development' : 'production', 9 | devtool: 'source-map', 10 | entry: { 11 | app: './app', 12 | }, 13 | output: { 14 | path: path.resolve('./dist'), 15 | filename: 'app.bundle.js', 16 | }, 17 | optimization: { 18 | noEmitOnErrors: true, 19 | }, 20 | plugins: [ 21 | new webpack.DefinePlugin({ 22 | 'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV) }, 23 | }), 24 | new MiniCssExtractPlugin({ filename: 'styles.css' }), 25 | ], 26 | resolve: { 27 | extensions: ['.js', '.jsx', '.mjs'], 28 | }, 29 | module: { 30 | rules: [ 31 | { 32 | test: /\.mjs$/, 33 | type: 'javascript/auto', 34 | }, 35 | { 36 | test: /\.jsx?$/, 37 | exclude: /node_modules/, 38 | use: [ 39 | { loader: 'babel-loader' }, 40 | { 41 | loader: '@wyw-in-js/webpack-loader', 42 | options: { sourceMap: dev }, 43 | }, 44 | ], 45 | }, 46 | { 47 | test: /\.css$/, 48 | use: [ 49 | 'css-hot-loader', 50 | MiniCssExtractPlugin.loader, 51 | { 52 | loader: 'css-loader', 53 | options: { sourceMap: dev }, 54 | }, 55 | ], 56 | }, 57 | { 58 | test: /\.(png|jpg|gif|svg)$/, 59 | use: [ 60 | { 61 | loader: 'file-loader', 62 | options: { 63 | esModule: false, 64 | }, 65 | }, 66 | ], 67 | }, 68 | ], 69 | }, 70 | }; 71 | -------------------------------------------------------------------------------- /greenkeeper.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": { 3 | "default": { 4 | "packages": [ 5 | "package.json", 6 | "website/package.json" 7 | ] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | collectCoverageFrom: ['src/*.ts'], 4 | transformIgnorePatterns: ['node_modules/(?!@linaria)'], 5 | testPathIgnorePatterns: ['/__utils__/'], 6 | }; 7 | -------------------------------------------------------------------------------- /link-wyw.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Find package.json files in the current folder (excluding node_modules) 4 | package_json_files=$(find . -name "package.json" ! -path "*/node_modules/*") 5 | 6 | # Loop through each package.json file found 7 | for package_json_file in $package_json_files; do 8 | # Get the directory path of the package.json file 9 | package_dir=$(dirname "$package_json_file") 10 | 11 | # Get all @linaria/* dependencies 12 | linaria_dependencies=$(cat "$package_json_file" | jq -r '(.dependencies + .devDependencies) | with_entries(select(.key | startswith("@wyw-in-js"))) | keys[]' 2>/dev/null) 13 | 14 | # Link @linaria dependencies 15 | if [[ $linaria_dependencies != "null" ]]; then 16 | for dep in $linaria_dependencies; do 17 | echo "Running pnpm link --global $dep in $package_dir" 18 | (cd "$package_dir" && pnpm link --global $dep) 19 | done 20 | fi 21 | done 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "umbrella", 3 | "version": "3.0.0-beta.19", 4 | "private": true, 5 | "homepage": "https://github.com/callstack/linaria#readme", 6 | "bugs": "https://github.com/callstack/linaria/issues", 7 | "repository": "git@github.com:callstack/linaria.git", 8 | "license": "MIT", 9 | "workspaces": [ 10 | "./packages/*", 11 | "./website" 12 | ], 13 | "scripts": { 14 | "add-contributor": "all-contributors add", 15 | "bootstrap": "pnpm install", 16 | "check:all": "turbo run check:all --output-logs=new-only && pnpm lint && pnpm sp:check", 17 | "clean": "del 'packages/*/{coverage,dist,esm,lib,types,tsconfig.tsbuildinfo}'", 18 | "lint": "eslint --ext .js,.ts,.tsx .", 19 | "prepare": "turbo run build --output-logs=new-only", 20 | "release": "pnpm run prepare && pnpm changeset publish", 21 | "sp:check": "syncpack", 22 | "sp:fix": "syncpack format && syncpack fix-mismatches", 23 | "sp:format": "syncpack format", 24 | "sp:list": "syncpack list-mismatches", 25 | "test": "turbo run test", 26 | "test:coverage": "turbo run test -- -- --coverage", 27 | "test:dts": "turbo run test:dts", 28 | "typecheck": "turbo run typecheck", 29 | "website": "pnpm --filter=linaria-website" 30 | }, 31 | "husky": { 32 | "hooks": { 33 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", 34 | "pre-commit": "pnpm check:all" 35 | } 36 | }, 37 | "resolutions": { 38 | "@typescript-eslint/experimental-utils": "^4.28.0", 39 | "git-raw-commits": "^2.0.3" 40 | }, 41 | "dependencies": {}, 42 | "devDependencies": { 43 | "@babel/cli": "^7.23.4", 44 | "@babel/core": "^7.23.5", 45 | "@babel/eslint-parser": "^7.23.3", 46 | "@babel/plugin-proposal-explicit-resource-management": "^7.23.3", 47 | "@babel/plugin-syntax-jsx": "^7.23.3", 48 | "@babel/preset-env": "^7.23.5", 49 | "@babel/preset-react": "^7.23.3", 50 | "@babel/preset-typescript": "^7.23.3", 51 | "@changesets/cli": "^2.22.0", 52 | "@commitlint/config-conventional": "^8.3.4", 53 | "@definitelytyped/dtslint": "^0.0.176", 54 | "@types/jest": "^28.1.0", 55 | "@types/node": "^17.0.39", 56 | "@types/resolve": "^1.20.6", 57 | "@typescript-eslint/eslint-plugin": "^6.6.0", 58 | "@typescript-eslint/parser": "^6.6.0", 59 | "all-contributors-cli": "^6.20.0", 60 | "babel-jest": "^29.6.2", 61 | "codecov": "^3.2.0", 62 | "commitlint": "^8.3.5", 63 | "cross-env": "^7.0.3", 64 | "del-cli": "^1.1.0", 65 | "eslint": "^8.48.0", 66 | "eslint-config-airbnb": "^19.0.4", 67 | "eslint-config-airbnb-typescript": "^17.1.0", 68 | "eslint-config-prettier": "^9.0.0", 69 | "eslint-plugin-import": "^2.28.1", 70 | "eslint-plugin-jsx-a11y": "^6.7.1", 71 | "eslint-plugin-prettier": "^5.0.0", 72 | "eslint-plugin-react": "^7.33.2", 73 | "eslint-plugin-react-hooks": "^4.6.0", 74 | "git-raw-commits": "^2.0.3", 75 | "husky": "^1.3.1", 76 | "jest": "^29.6.2", 77 | "prettier": "^3.0.3", 78 | "react": "^16.14.0", 79 | "syncpack": "^11.2.1", 80 | "tsup": "^7.2.0", 81 | "turbo": "^1.10.13", 82 | "typescript": "^5.2.2" 83 | }, 84 | "engines": { 85 | "node": ">=16.0.0", 86 | "pnpm": "^9.0.0" 87 | }, 88 | "packageManager": "pnpm@9.15.9+sha256.cf86a7ad764406395d4286a6d09d730711720acc6d93e9dce9ac7ac4dc4a28a7" 89 | } 90 | -------------------------------------------------------------------------------- /packages/atomic/README.md: -------------------------------------------------------------------------------- 1 |

2 | Linaria 3 |

4 | 5 |

6 | Zero-runtime CSS in JS library. 7 |

8 | 9 | --- 10 | 11 | ### 📖 Please refer to the [GitHub](https://github.com/callstack/linaria#readme) for full documentation. 12 | 13 | ## Features 14 | 15 | - Write CSS in JS, but with **zero runtime**, CSS is extracted to CSS files during build 16 | - Familiar **CSS syntax** with Sass like nesting 17 | - Use **dynamic prop based styles** with the React bindings, uses CSS variables behind the scenes 18 | - Easily find where the style was defined with **CSS sourcemaps** 19 | - **Lint your CSS** in JS with [stylelint](https://github.com/stylelint/stylelint) 20 | - Use **JavaScript for logic**, no CSS preprocessor needed 21 | - Optionally use any **CSS preprocessor** such as Sass or PostCSS 22 | 23 | **[Why use Linaria](../../docs/BENEFITS.md)** 24 | 25 | ## Installation 26 | 27 | ```sh 28 | npm install @linaria/core @linaria/react @linaria/babel-preset 29 | ``` 30 | 31 | or 32 | 33 | ```sh 34 | yarn add @linaria/core @linaria/react @linaria/babel-preset 35 | ``` 36 | -------------------------------------------------------------------------------- /packages/atomic/babel.config.js: -------------------------------------------------------------------------------- 1 | const config = require('../../babel.config'); 2 | 3 | module.exports = config; 4 | -------------------------------------------------------------------------------- /packages/atomic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@linaria/atomic", 3 | "version": "6.3.0", 4 | "description": "Blazing fast zero-runtime CSS in JS library", 5 | "keywords": [ 6 | "css", 7 | "css-in-js", 8 | "linaria", 9 | "react", 10 | "styled-components" 11 | ], 12 | "homepage": "https://github.com/callstack/linaria#readme", 13 | "bugs": "https://github.com/callstack/linaria/issues", 14 | "repository": "git@github.com:callstack/linaria.git", 15 | "license": "MIT", 16 | "sideEffects": false, 17 | "exports": { 18 | "./package.json": "./package.json", 19 | ".": { 20 | "types": "./types/index.d.ts", 21 | "import": "./dist/index.mjs", 22 | "default": "./dist/index.js" 23 | }, 24 | "./*": { 25 | "types": "./types/*.d.ts", 26 | "import": "./dist/*.mjs", 27 | "default": "./dist/*.js" 28 | } 29 | }, 30 | "main": "dist/index.js", 31 | "module": "dist/index.mjs", 32 | "types": "types/index.d.ts", 33 | "files": [ 34 | "dist/", 35 | "processors/", 36 | "types/" 37 | ], 38 | "wyw-in-js": { 39 | "tags": { 40 | "css": "./dist/processors/css.js", 41 | "styled": "./dist/processors/styled.js" 42 | } 43 | }, 44 | "scripts": { 45 | "build": "pnpm build:dist && pnpm build:declarations", 46 | "build:declarations": "tsc --emitDeclarationOnly --outDir types", 47 | "build:dist": "tsup --format cjs,esm", 48 | "typecheck": "tsc --noEmit --composite false", 49 | "watch": "pnpm build:dist --watch & pnpm build:declarations --watch" 50 | }, 51 | "dependencies": { 52 | "@linaria/core": "workspace:^", 53 | "@linaria/react": "workspace:^", 54 | "@wyw-in-js/processor-utils": "^0.6.0", 55 | "@wyw-in-js/shared": "^0.6.0", 56 | "known-css-properties": "^0.24.0", 57 | "postcss": "^8.4.31", 58 | "stylis": "^4.3.0", 59 | "ts-invariant": "^0.10.3" 60 | }, 61 | "devDependencies": { 62 | "@babel/types": "^7.23.5", 63 | "@types/node": "^17.0.39", 64 | "@types/stylis": "^4.2.1" 65 | }, 66 | "peerDependencies": { 67 | "react": ">=16" 68 | }, 69 | "peerDependenciesMeta": { 70 | "react": { 71 | "optional": true 72 | } 73 | }, 74 | "engines": { 75 | "node": ">=16.0.0" 76 | }, 77 | "publishConfig": { 78 | "access": "public" 79 | }, 80 | "tsup": { 81 | "entry": [ 82 | "src/index.ts", 83 | "src/processors/css.ts", 84 | "src/processors/styled.ts" 85 | ], 86 | "splitting": false, 87 | "sourcemap": true, 88 | "clean": true 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /packages/atomic/processors/css.js: -------------------------------------------------------------------------------- 1 | Object.defineProperty(exports, '__esModule', { 2 | value: true, 3 | }); 4 | 5 | exports.default = require('../dist/processors/css').default; 6 | -------------------------------------------------------------------------------- /packages/atomic/processors/styled.js: -------------------------------------------------------------------------------- 1 | Object.defineProperty(exports, '__esModule', { 2 | value: true, 3 | }); 4 | 5 | exports.default = require('../dist/processors/styled').default; 6 | -------------------------------------------------------------------------------- /packages/atomic/src/CSSProperties.ts: -------------------------------------------------------------------------------- 1 | export type CSSProperties = { 2 | [key: string]: string | number | CSSProperties; 3 | }; 4 | -------------------------------------------------------------------------------- /packages/atomic/src/css.ts: -------------------------------------------------------------------------------- 1 | import type { LinariaClassName } from '@linaria/core'; 2 | 3 | import type { CSSProperties } from './CSSProperties'; 4 | 5 | type CSS = ( 6 | strings: TemplateStringsArray, 7 | ...exprs: Array 8 | ) => LinariaClassName; 9 | 10 | let idx = 0; 11 | 12 | export const css: CSS = () => { 13 | if (process.env.NODE_ENV === 'test') { 14 | // eslint-disable-next-line no-plusplus 15 | return `mocked-atomic-css-${idx++}` as LinariaClassName; 16 | } 17 | 18 | throw new Error( 19 | 'Using the "css" tag in runtime is not supported. Make sure you have set up the Babel plugin correctly.' 20 | ); 21 | }; 22 | 23 | export default css; 24 | -------------------------------------------------------------------------------- /packages/atomic/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as css } from './css'; 2 | export { styled } from '@linaria/react'; 3 | export { cx } from '@linaria/core'; 4 | 5 | export type { CSSProperties } from './CSSProperties'; 6 | -------------------------------------------------------------------------------- /packages/atomic/src/processors/css.ts: -------------------------------------------------------------------------------- 1 | import type { SourceLocation } from '@babel/types'; 2 | import type { Rules, ValueCache } from '@wyw-in-js/processor-utils'; 3 | import { logger } from '@wyw-in-js/shared'; 4 | 5 | import CssProcessor from '@linaria/core/processors/css'; 6 | 7 | import atomize from './helpers/atomize'; 8 | 9 | const debug = logger.extend('AtomicCssProcessor'); 10 | 11 | export default class AtomicCssProcessor extends CssProcessor { 12 | #classes: string | undefined; 13 | 14 | private get classes(): string { 15 | if (typeof this.#classes !== 'undefined') { 16 | return this.#classes; 17 | } 18 | 19 | throw new Error('Styles are not extracted yet. Please call `build` first.'); 20 | } 21 | 22 | public override doRuntimeReplacement(): void { 23 | this.replacer(this.astService.stringLiteral(this.classes), false); 24 | } 25 | 26 | public override extractRules( 27 | valueCache: ValueCache, 28 | cssText: string, 29 | loc?: SourceLocation | null 30 | ): Rules { 31 | const rules: Rules = {}; 32 | 33 | const atomicRules = atomize(cssText, false); 34 | atomicRules.forEach((rule) => { 35 | // eslint-disable-next-line no-param-reassign 36 | rules[rule.cssText] = { 37 | cssText: rule.cssText, 38 | start: loc?.start ?? null, 39 | className: this.className!, 40 | displayName: this.displayName!, 41 | atom: true, 42 | }; 43 | 44 | debug('extracted-atomic-rule:\n%s', rule.cssText); 45 | }); 46 | 47 | this.#classes = atomicRules 48 | // Some atomic rules produced (eg. keyframes) don't have class names, and they also don't need to appear in the object 49 | .filter((rule) => !!rule.className) 50 | .map((rule) => rule.className!) 51 | .join(' '); 52 | 53 | return rules; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/atomic/src/processors/helpers/atomize.ts: -------------------------------------------------------------------------------- 1 | import { slugify } from '@wyw-in-js/shared'; 2 | import { all as knownProperties } from 'known-css-properties'; 3 | import type { Document, AtRule, Container, Rule } from 'postcss'; 4 | import postcss from 'postcss'; 5 | import { compile, serialize, stringify } from 'stylis'; 6 | 7 | import { getPropertyPriority } from './propertyPriority'; 8 | 9 | const knownPropertiesMap = knownProperties.reduce( 10 | (acc: { [property: string]: number }, property, i) => { 11 | acc[property] = i; 12 | return acc; 13 | }, 14 | {} 15 | ); 16 | 17 | function hashProperty(property: string) { 18 | const index = knownPropertiesMap[property]; 19 | // If it's a known property, let's use the index to cut down the length of the hash. 20 | // otherwise, slugify 21 | if (index !== undefined) { 22 | return index.toString(36); // base 36 so that we get a-z,0-9 23 | } 24 | return slugify(property); 25 | } 26 | 27 | const parseCss = (cssText: string) => { 28 | try { 29 | return postcss.parse(cssText); 30 | } catch (e) { 31 | if (e instanceof Error) { 32 | throw new Error(`Error parsing CSS: ${e.message}\nCSS:\n${cssText}`); 33 | } 34 | 35 | throw new Error(`Unknown error parsing CSS.\nCSS:\n${cssText}`); 36 | } 37 | }; 38 | 39 | export default function atomize(cssText: string, hasPriority = false) { 40 | const atomicRules: { 41 | className?: string; 42 | cssText: string; 43 | property: string; 44 | }[] = []; 45 | 46 | const stylesheet = parseCss(cssText); 47 | 48 | // We want to extract all keyframes and leave them as-is. 49 | // This isn't scoped locally yet 50 | stylesheet.walkAtRules('keyframes', (atRule) => { 51 | atRule.remove(); 52 | atomicRules.push({ 53 | property: atRule.name, 54 | cssText: atRule.toString(), 55 | }); 56 | }); 57 | 58 | stylesheet.walkDecls((decl) => { 59 | let thisParent: Document | Container | undefined = decl.parent; 60 | const parents: (Document | Container)[] = []; 61 | const atomicProperty = [decl.prop]; 62 | let hasAtRule = false; 63 | 64 | // Traverse the declarations parents, and collect them all. 65 | while (thisParent && thisParent !== stylesheet) { 66 | parents.unshift(thisParent); 67 | if (thisParent.type === 'atrule') { 68 | hasAtRule = true; 69 | // @media queries, @supports etc. 70 | atomicProperty.push( 71 | (thisParent as AtRule).name, 72 | (thisParent as AtRule).params 73 | ); 74 | } else if (thisParent.type === 'rule') { 75 | // pseudo classes etc. 76 | atomicProperty.push((thisParent as Rule).selector); 77 | } 78 | 79 | thisParent = thisParent.parent; 80 | } 81 | 82 | // Create a new stylesheet that contains *just* the extracted atomic rule and wrapping selectors, eg. 83 | // `@media (max-width: 400px) { background: red; }`, or 84 | // `&:hover { background: red; }`, or 85 | // `background: red;` 86 | // We do this so we can run it through stylis, to produce a full atom, eg. 87 | // `@media (max-width: 400px) { .atm_foo { background: red; } }` 88 | const root = postcss.root(); 89 | let container: Document | Container = root; 90 | parents.forEach((parent) => { 91 | const newNode = parent.clone(); 92 | newNode.removeAll(); 93 | container.append(newNode); 94 | container = newNode; 95 | }); 96 | container.append(decl.clone()); 97 | 98 | const css = root.toString(); 99 | const propertySlug = hashProperty([...atomicProperty].join(';')); 100 | const valueSlug = slugify(decl.value); 101 | const className = `atm_${propertySlug}_${valueSlug}`; 102 | 103 | const propertyPriority = 104 | getPropertyPriority(decl.prop) + 105 | (hasAtRule ? 1 : 0) + 106 | (hasPriority ? 1 : 0); 107 | const selector = `.${className}`.repeat(propertyPriority); 108 | const processedCss = serialize(compile(`${selector} {${css}}`), stringify); 109 | 110 | atomicRules.push({ 111 | property: atomicProperty.join(' '), 112 | className, 113 | cssText: processedCss, 114 | }); 115 | }); 116 | 117 | return atomicRules; 118 | } 119 | -------------------------------------------------------------------------------- /packages/atomic/src/processors/styled.ts: -------------------------------------------------------------------------------- 1 | import type { SourceLocation } from '@babel/types'; 2 | import type { Rules, ValueCache } from '@wyw-in-js/processor-utils'; 3 | import { logger, hasEvalMeta } from '@wyw-in-js/shared'; 4 | 5 | import type { IProps } from '@linaria/react/processors/styled'; 6 | import StyledProcessor from '@linaria/react/processors/styled'; 7 | 8 | import atomize from './helpers/atomize'; 9 | 10 | const debug = logger.extend('AtomicStyledProcessor'); 11 | 12 | export default class AtomicStyledProcessor extends StyledProcessor { 13 | #classes: string | undefined; 14 | 15 | private get classes(): string { 16 | if (this.#classes) { 17 | return this.#classes; 18 | } 19 | 20 | throw new Error( 21 | 'Styles are not extracted yet. Please call `extractRules` first.' 22 | ); 23 | } 24 | 25 | public override extractRules( 26 | valueCache: ValueCache, 27 | cssText: string, 28 | loc?: SourceLocation | null 29 | ): Rules { 30 | const rules: Rules = {}; 31 | 32 | const wrappedValue = 33 | typeof this.component === 'string' 34 | ? null 35 | : valueCache.get(this.component.node.name); 36 | 37 | const atomicRules = atomize(cssText, hasEvalMeta(wrappedValue)); 38 | atomicRules.forEach((rule) => { 39 | // eslint-disable-next-line no-param-reassign 40 | rules[rule.cssText] = { 41 | cssText: rule.cssText, 42 | start: loc?.start ?? null, 43 | className: this.className, 44 | displayName: this.displayName, 45 | atom: true, 46 | }; 47 | 48 | debug('extracted-atomic-rule:\n%s', rule.cssText); 49 | }); 50 | 51 | this.#classes = atomicRules 52 | // Some atomic rules produced (eg. keyframes) don't have class names, and they also don't need to appear in the object 53 | .filter((rule) => !!rule.className) 54 | .map((rule) => rule.className!) 55 | .join(' '); 56 | 57 | return rules; 58 | } 59 | 60 | protected override getProps(): IProps { 61 | const props = super.getProps(); 62 | props.class = [this.classes, this.className].filter(Boolean).join(' '); 63 | props.atomic = true; 64 | return props; 65 | } 66 | 67 | protected override getVariableId( 68 | source: string, 69 | unit: string, 70 | precedingCss: string 71 | ): string { 72 | const id = this.getCustomVariableId(source, unit, precedingCss); 73 | if (id) { 74 | return id; 75 | } 76 | 77 | const context = this.getVariableContext(source, unit, precedingCss); 78 | // id is based on the slugified value 79 | return context.valueSlug; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /packages/atomic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "paths": {}, 5 | "rootDir": "src/", 6 | "types": ["node"] 7 | }, 8 | "references": [{ "path": "../core" }, { "path": "../react" }] 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 |

2 | Linaria 3 |

4 | 5 |

6 | Zero-runtime CSS in JS library. 7 |

8 | 9 | --- 10 | 11 | ### 📖 Please refer to the [GitHub](https://github.com/callstack/linaria#readme) for full documentation. 12 | 13 | ## Features 14 | 15 | - Write CSS in JS, but with **zero runtime**, CSS is extracted to CSS files during build 16 | - Familiar **CSS syntax** with Sass like nesting 17 | - Use **dynamic prop based styles** with the React bindings, uses CSS variables behind the scenes 18 | - Easily find where the style was defined with **CSS sourcemaps** 19 | - **Lint your CSS** in JS with [stylelint](https://github.com/stylelint/stylelint) 20 | - Use **JavaScript for logic**, no CSS preprocessor needed 21 | - Optionally use any **CSS preprocessor** such as Sass or PostCSS 22 | 23 | **[Why use Linaria](../../docs/BENEFITS.md)** 24 | 25 | ## Installation 26 | 27 | ```sh 28 | npm install @linaria/core @linaria/react @linaria/babel-preset 29 | ``` 30 | 31 | or 32 | 33 | ```sh 34 | yarn add @linaria/core @linaria/react @linaria/babel-preset 35 | ``` 36 | -------------------------------------------------------------------------------- /packages/core/__dtslint__/core.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | import { css, cx } from '../src'; 3 | 4 | // $ExpectType LinariaClassName 5 | const class1 = css``; 6 | 7 | const activeClass = css``; 8 | 9 | // $ExpectType string 10 | const combined1 = cx(class1, 'active'); 11 | 12 | // $ExpectType LinariaClassName 13 | const combined2 = cx(class1, activeClass); 14 | -------------------------------------------------------------------------------- /packages/core/__dtslint__/index.d.ts: -------------------------------------------------------------------------------- 1 | // dtslint wants to see index.d.ts. Well, here it is. 2 | declare const linaria: unknown; 3 | -------------------------------------------------------------------------------- /packages/core/__dtslint__/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/__dtslint__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "lib": ["es6"], 5 | "target": "ES2015", 6 | "esModuleInterop": true, 7 | "noImplicitAny": true, 8 | "noImplicitThis": true, 9 | "strictNullChecks": true, 10 | "strictFunctionTypes": true, 11 | "noEmit": true, 12 | 13 | "baseUrl": "./" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/__tests__/cx.test.ts: -------------------------------------------------------------------------------- 1 | import cx from '../src/cx'; 2 | 3 | it('should filter falsy values', () => { 4 | expect(cx('1', 'test', false, '2', 0, '', null, undefined, '3')).toBe( 5 | '1 test 2 3' 6 | ); 7 | }); 8 | 9 | it('should join atoms together, de-duplicating by property and joining the values', () => { 10 | expect(cx('atm_a_class1 atm_b_class2', 'atm_a_class3')).toBe( 11 | 'atm_a_class3 atm_b_class2' 12 | ); 13 | }); 14 | 15 | it('should join atoms and non atoms together at the same time', () => { 16 | expect( 17 | cx('atm_a_1', 'atm_b_2', false, '2', 0, '', null, undefined, '3', 'atm_b_3') 18 | ).toBe('atm_a_1 atm_b_3 2 3'); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/core/__tests__/detect-core-js.test.ts: -------------------------------------------------------------------------------- 1 | import cp from 'child_process'; 2 | 3 | const waitForProcess = async (process: cp.ChildProcess) => { 4 | return new Promise((resolve) => { 5 | let output = ''; 6 | process.stdout?.on('data', (chunk) => { 7 | output += chunk.toString(); 8 | }); 9 | process.on('close', () => { 10 | resolve(output); 11 | }); 12 | }); 13 | }; 14 | 15 | it('Ensures that package do not include core-js dependency after build', async () => { 16 | // eslint-disable-next-line import/no-extraneous-dependencies 17 | const packageJSON = require('@linaria/core/package.json'); 18 | const buildScript = packageJSON.scripts['build:corejs-test']; 19 | 20 | const proc = cp.exec(buildScript, { 21 | env: { 22 | ...process.env, 23 | DEBUG_CORE_JS: 'true', 24 | }, 25 | }); 26 | const result = await waitForProcess(proc); 27 | // run `DEBUG_CORE_JS=true yarn build:lib` to debug issues with introduced core-js dependency 28 | expect(result).not.toContain( 29 | 'The corejs3 polyfill added the following polyfills' 30 | ); 31 | expect(result).toContain( 32 | 'Based on your code and targets, the corejs3 polyfill did not add any polyfill' 33 | ); 34 | }, 30000); 35 | -------------------------------------------------------------------------------- /packages/core/babel.config.js: -------------------------------------------------------------------------------- 1 | const config = require('../../babel.config'); 2 | 3 | module.exports = config; 4 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@linaria/core", 3 | "version": "6.3.0", 4 | "description": "Blazing fast zero-runtime CSS in JS library", 5 | "keywords": [ 6 | "css", 7 | "css-in-js", 8 | "linaria", 9 | "react", 10 | "styled-components" 11 | ], 12 | "homepage": "https://github.com/callstack/linaria#readme", 13 | "bugs": "https://github.com/callstack/linaria/issues", 14 | "repository": "git@github.com:callstack/linaria.git", 15 | "license": "MIT", 16 | "sideEffects": false, 17 | "exports": { 18 | "./package.json": "./package.json", 19 | ".": { 20 | "types": "./types/index.d.ts", 21 | "import": "./dist/index.mjs", 22 | "default": "./dist/index.js" 23 | }, 24 | "./*": { 25 | "types": "./types/*.d.ts", 26 | "import": "./dist/*.mjs", 27 | "default": "./dist/*.js" 28 | } 29 | }, 30 | "main": "dist/index.js", 31 | "module": "dist/index.mjs", 32 | "types": "types/index.d.ts", 33 | "typesVersions": { 34 | "*": { 35 | "processors/*": [ 36 | "./types/processors/*.d.ts" 37 | ] 38 | } 39 | }, 40 | "files": [ 41 | "dist/", 42 | "processors/", 43 | "types/" 44 | ], 45 | "wyw-in-js": { 46 | "tags": { 47 | "css": "./dist/processors/css.js" 48 | } 49 | }, 50 | "scripts": { 51 | "build": "pnpm build:dist && pnpm build:declarations", 52 | "build:corejs-test": "cross-env NODE_ENV=legacy babel src --out-dir lib --extensions '.js,.jsx,.ts,.tsx' --ignore \"src/processors/**/*\"", 53 | "build:declarations": "tsc --emitDeclarationOnly --outDir types", 54 | "build:dist": "tsup --format cjs,esm", 55 | "test": "jest --config ../../jest.config.js --rootDir .", 56 | "test:dts": "dtslint --localTs ../../node_modules/typescript/lib __dtslint__", 57 | "typecheck": "tsc --noEmit --composite false", 58 | "watch": "pnpm build:dist --watch & pnpm build:declarations --watch" 59 | }, 60 | "dependencies": { 61 | "@wyw-in-js/processor-utils": "^0.6.0" 62 | }, 63 | "devDependencies": { 64 | "@babel/traverse": "^7.23.5", 65 | "@babel/types": "^7.23.5", 66 | "@types/babel__core": "^7.20.5", 67 | "@types/babel__traverse": "^7.20.4", 68 | "@types/node": "^17.0.39" 69 | }, 70 | "engines": { 71 | "node": ">=16.0.0" 72 | }, 73 | "publishConfig": { 74 | "access": "public" 75 | }, 76 | "tsup": { 77 | "entry": [ 78 | "src/index.ts", 79 | "src/processors/css.ts" 80 | ], 81 | "splitting": false, 82 | "sourcemap": true, 83 | "clean": true 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /packages/core/processors/css.js: -------------------------------------------------------------------------------- 1 | Object.defineProperty(exports, '__esModule', { 2 | value: true, 3 | }); 4 | 5 | exports.default = require('../dist/processors/css').default; 6 | -------------------------------------------------------------------------------- /packages/core/src/CSSProperties.ts: -------------------------------------------------------------------------------- 1 | export type CSSProperties = { 2 | [key: string]: string | number | CSSProperties; 3 | }; 4 | -------------------------------------------------------------------------------- /packages/core/src/css.ts: -------------------------------------------------------------------------------- 1 | import type { CSSProperties } from './CSSProperties'; 2 | import type { LinariaClassName } from './cx'; 3 | 4 | type WYWEvalMeta = { __wyw_meta: unknown }; // simplified version of WYWEvalMeta from @wyw-in-js/shared 5 | 6 | type CSS = ( 7 | strings: TemplateStringsArray, 8 | ...exprs: Array 9 | ) => LinariaClassName; 10 | 11 | let idx = 0; 12 | 13 | const css: CSS = () => { 14 | if (process.env.NODE_ENV === 'test') { 15 | // eslint-disable-next-line no-plusplus 16 | return `mocked-css-${idx++}` as LinariaClassName; 17 | } 18 | 19 | throw new Error( 20 | 'Using the "css" tag in runtime is not supported. Make sure you have set up the Babel plugin correctly.' 21 | ); 22 | }; 23 | 24 | export default css; 25 | -------------------------------------------------------------------------------- /packages/core/src/cx.ts: -------------------------------------------------------------------------------- 1 | export type LinariaClassName = string & { __linariaClassName: true }; 2 | 3 | export type ClassName = T | false | void | null | 0 | ''; 4 | 5 | interface ICX { 6 | (...classNames: ClassName[]): LinariaClassName; 7 | (...classNames: ClassName[]): string; 8 | } 9 | /** 10 | * Takes a list of class names and filters for truthy ones, joining them into a single class name for convenience. 11 | * eg. 12 | * ```js 13 | * cx('red', isBig && 'big') // returns 'red big' if `isBig` is true, otherwise returns 'red' 14 | * ``` 15 | * If space separated atomic styles are provided, they are deduplicated according to the first hashed valued: 16 | * 17 | * ```js 18 | * cx('atm_a_class1 atm_b_class2', 'atm_a_class3') // returns `atm_a_class3 atm_b_class2` 19 | * ``` 20 | * 21 | * @returns the combined, space separated class names that can be applied directly to the class attribute 22 | */ 23 | const cx: ICX = function cx() { 24 | const presentClassNames: (ClassName | ClassName)[] = 25 | Array.prototype.slice 26 | // eslint-disable-next-line prefer-rest-params 27 | .call(arguments) 28 | .filter(Boolean); 29 | 30 | const atomicClasses: { [k: string]: string } = {}; 31 | const nonAtomicClasses: string[] = []; 32 | presentClassNames.forEach((arg) => { 33 | // className could be the output of a previous cx call, so split by ' ' first 34 | const individualClassNames = arg ? arg.split(' ') : []; 35 | 36 | individualClassNames.forEach((className) => { 37 | if (className.startsWith('atm_')) { 38 | const [, keyHash] = className.split('_'); 39 | atomicClasses[keyHash] = className; 40 | } else { 41 | nonAtomicClasses.push(className); 42 | } 43 | }); 44 | }); 45 | 46 | const result: string[] = []; 47 | 48 | // eslint-disable-next-line no-restricted-syntax 49 | for (const keyHash in atomicClasses) { 50 | if (Object.prototype.hasOwnProperty.call(atomicClasses, keyHash)) { 51 | result.push(atomicClasses[keyHash]); 52 | } 53 | } 54 | 55 | result.push(...nonAtomicClasses); 56 | 57 | return result.join(' ') as LinariaClassName; 58 | }; 59 | 60 | export default cx; 61 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as css } from './css'; 2 | export { default as cx } from './cx'; 3 | export type { CSSProperties } from './CSSProperties'; 4 | export type { LinariaClassName } from './cx'; 5 | -------------------------------------------------------------------------------- /packages/core/src/processors/css.ts: -------------------------------------------------------------------------------- 1 | import type { SourceLocation, StringLiteral } from '@babel/types'; 2 | import type { Rules, ValueCache } from '@wyw-in-js/processor-utils'; 3 | import { TaggedTemplateProcessor } from '@wyw-in-js/processor-utils'; 4 | 5 | export default class CssProcessor extends TaggedTemplateProcessor { 6 | public override get asSelector(): string { 7 | return this.className; 8 | } 9 | 10 | public override get value(): StringLiteral { 11 | return this.astService.stringLiteral(this.className); 12 | } 13 | 14 | // eslint-disable-next-line class-methods-use-this 15 | public override addInterpolation( 16 | node: unknown, 17 | precedingCss: string, 18 | source: string 19 | ): string { 20 | throw new Error( 21 | `css tag cannot handle '${source}' as an interpolated value` 22 | ); 23 | } 24 | 25 | public override doEvaltimeReplacement(): void { 26 | this.replacer(this.value, false); 27 | } 28 | 29 | public override doRuntimeReplacement(): void { 30 | this.replacer(this.astService.stringLiteral(this.className), false); 31 | } 32 | 33 | public override extractRules( 34 | valueCache: ValueCache, 35 | cssText: string, 36 | loc?: SourceLocation | null 37 | ): Rules { 38 | const rules: Rules = {}; 39 | 40 | const selector = `.${this.className}`; 41 | 42 | rules[selector] = { 43 | cssText, 44 | className: this.className, 45 | displayName: this.displayName, 46 | start: loc?.start ?? null, 47 | }; 48 | 49 | return rules; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "paths": {}, 5 | "rootDir": "src/", 6 | "types": ["node"] 7 | }, 8 | "references": [] 9 | } 10 | -------------------------------------------------------------------------------- /packages/interop/README.md: -------------------------------------------------------------------------------- 1 |

2 | Linaria 3 |

4 | 5 |

6 | Zero-runtime CSS in JS library. 7 |

8 | 9 | --- 10 | 11 | # `@linaria/babel-plugin-interop` 12 | 13 | This plugin allows to interpolate Linaria components inside styled-components and Emotion: 14 | ```javascript 15 | import styled from 'styled-components'; 16 | import { Title } from './Title.styled'; // Linaria component 17 | 18 | const Article = () => { /* … */ }; 19 | 20 | export default styled(Article)` 21 | & > ${Title} { 22 | color: green; 23 | } 24 | `; 25 | 26 | ``` 27 | 28 | ## Quick start 29 | 30 | Install the plugin first: 31 | 32 | ``` 33 | npm install --save-dev @linaria/babel-plugin-interop 34 | ``` 35 | 36 | Then add `@linaria/interop` to your babel configuration *before* `styled-components`: 37 | 38 | ```JSON 39 | { 40 | "plugins": [ 41 | ["@linaria/interop", { "library": "styled-components" }], 42 | "styled-components" 43 | ] 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /packages/interop/__tests__/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`styled-components css 1`] = ` 4 | ""use strict"; 5 | 6 | Object.defineProperty(exports, "__esModule", { 7 | value: true 8 | }); 9 | exports.default = void 0; 10 | var _styledComponents = require("styled-components"); 11 | var _Title = _interopRequireDefault(require("./Title")); 12 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 13 | var _default = exports.default = (0, _styledComponents.css)\` 14 | & > \${(i => i && i.__wyw_meta ? '.' + i.__wyw_meta.className : i)(_Title.default)} { 15 | color: red; 16 | } 17 | \`;" 18 | `; 19 | 20 | exports[`styled-components keeps linaria 1`] = ` 21 | ""use strict"; 22 | 23 | Object.defineProperty(exports, "__esModule", { 24 | value: true 25 | }); 26 | exports.default = void 0; 27 | var _react = _interopRequireDefault(require("linaria/react")); 28 | var _Title = _interopRequireDefault(require("./Title")); 29 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 30 | var _default = exports.default = _react.default.h1\` 31 | & > \${_Title.default} { 32 | color: red; 33 | } 34 | \`;" 35 | `; 36 | 37 | exports[`styled-components member expression as selector 1`] = ` 38 | ""use strict"; 39 | 40 | Object.defineProperty(exports, "__esModule", { 41 | value: true 42 | }); 43 | exports.default = void 0; 44 | var _styledComponents = _interopRequireDefault(require("styled-components")); 45 | var _Title = _interopRequireDefault(require("./Title")); 46 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 47 | var _default = exports.default = _styledComponents.default.h1\` 48 | & > \${(i => i && i.__wyw_meta ? '.' + i.__wyw_meta.className : i)(_Title.default.Small)} { 49 | color: red; 50 | } 51 | \`;" 52 | `; 53 | 54 | exports[`styled-components styled(Cmp) 1`] = ` 55 | ""use strict"; 56 | 57 | Object.defineProperty(exports, "__esModule", { 58 | value: true 59 | }); 60 | exports.default = void 0; 61 | var _styledComponents = _interopRequireDefault(require("styled-components")); 62 | var _Cmp = _interopRequireDefault(require("./Cmp")); 63 | var _Title = _interopRequireDefault(require("./Title")); 64 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 65 | var _default = exports.default = (0, _styledComponents.default)(_Cmp.default)\` 66 | & > \${(i => i && i.__wyw_meta ? '.' + i.__wyw_meta.className : i)(_Title.default)} { 67 | color: red; 68 | } 69 | \`;" 70 | `; 71 | 72 | exports[`styled-components styled(Cmp).attrs({}) 1`] = ` 73 | ""use strict"; 74 | 75 | Object.defineProperty(exports, "__esModule", { 76 | value: true 77 | }); 78 | exports.default = void 0; 79 | var _styledComponents = _interopRequireDefault(require("styled-components")); 80 | var _Cmp = _interopRequireDefault(require("./Cmp")); 81 | var _Title = _interopRequireDefault(require("./Title")); 82 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 83 | var _default = exports.default = (0, _styledComponents.default)(_Cmp.default).attrs(() => ({}))\` 84 | & > \${(i => i && i.__wyw_meta ? '.' + i.__wyw_meta.className : i)(_Title.default)} { 85 | color: red; 86 | } 87 | \`;" 88 | `; 89 | 90 | exports[`styled-components styled.h1 1`] = ` 91 | ""use strict"; 92 | 93 | Object.defineProperty(exports, "__esModule", { 94 | value: true 95 | }); 96 | exports.default = void 0; 97 | var _styledComponents = _interopRequireDefault(require("styled-components")); 98 | var _Title = _interopRequireDefault(require("./Title")); 99 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 100 | var _default = exports.default = _styledComponents.default.h1\` 101 | & > \${(i => i && i.__wyw_meta ? '.' + i.__wyw_meta.className : i)(_Title.default)} { 102 | color: red; 103 | } 104 | \`;" 105 | `; 106 | 107 | exports[`styled-components styled.h1.attrs({}) 1`] = ` 108 | ""use strict"; 109 | 110 | Object.defineProperty(exports, "__esModule", { 111 | value: true 112 | }); 113 | exports.default = void 0; 114 | var _styledComponents = _interopRequireDefault(require("styled-components")); 115 | var _Title = _interopRequireDefault(require("./Title")); 116 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 117 | var _default = exports.default = _styledComponents.default.h1.attrs(() => ({}))\` 118 | & > \${(i => i && i.__wyw_meta ? '.' + i.__wyw_meta.className : i)(_Title.default)} { 119 | color: red; 120 | } 121 | \`;" 122 | `; 123 | -------------------------------------------------------------------------------- /packages/interop/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | const babel = require('@babel/core'); 2 | const dedent = require('dedent'); 3 | 4 | const plugin = require('../src'); 5 | 6 | const getCode = (src: string) => { 7 | const { code } = babel.transform(dedent(src), { 8 | filename: 'test.js', 9 | babelrc: false, 10 | plugins: [plugin], 11 | }); 12 | return code; 13 | }; 14 | 15 | describe('styled-components', () => { 16 | it('keeps linaria', () => { 17 | const code = getCode(` 18 | import styled from "linaria/react"; 19 | import Title from "./Title"; 20 | 21 | export default styled.h1\` 22 | & > ${'${Title}'} { 23 | color: red; 24 | } 25 | \`; 26 | `); 27 | 28 | expect(code).toMatchSnapshot(); 29 | }); 30 | 31 | it('css', () => { 32 | const code = getCode(` 33 | import { css } from "styled-components"; 34 | import Title from "./Title"; 35 | 36 | export default css\` 37 | & > ${'${Title}'} { 38 | color: red; 39 | } 40 | \`; 41 | `); 42 | 43 | expect(code).toMatchSnapshot(); 44 | }); 45 | 46 | it('styled.h1', () => { 47 | const code = getCode(` 48 | import styled from "styled-components"; 49 | import Title from "./Title"; 50 | 51 | export default styled.h1\` 52 | & > ${'${Title}'} { 53 | color: red; 54 | } 55 | \`; 56 | `); 57 | 58 | expect(code).toMatchSnapshot(); 59 | }); 60 | 61 | it('member expression as selector', () => { 62 | const code = getCode(` 63 | import styled from "styled-components"; 64 | import Title from "./Title"; 65 | 66 | export default styled.h1\` 67 | & > ${'${Title.Small}'} { 68 | color: red; 69 | } 70 | \`; 71 | `); 72 | 73 | expect(code).toMatchSnapshot(); 74 | }); 75 | 76 | it('styled(Cmp)', () => { 77 | const code = getCode(` 78 | import styled from "styled-components"; 79 | import Cmp from "./Cmp"; 80 | import Title from "./Title"; 81 | 82 | export default styled(Cmp)\` 83 | & > ${'${Title}'} { 84 | color: red; 85 | } 86 | \`; 87 | `); 88 | 89 | expect(code).toMatchSnapshot(); 90 | }); 91 | 92 | it('styled(Cmp).attrs({})', () => { 93 | const code = getCode(` 94 | import styled from "styled-components"; 95 | import Cmp from "./Cmp"; 96 | import Title from "./Title"; 97 | 98 | export default styled(Cmp).attrs(() => ({}))\` 99 | & > ${'${Title}'} { 100 | color: red; 101 | } 102 | \`; 103 | `); 104 | 105 | expect(code).toMatchSnapshot(); 106 | }); 107 | 108 | it('styled.h1.attrs({})', () => { 109 | const code = getCode(` 110 | import styled from "styled-components"; 111 | import Title from "./Title"; 112 | 113 | export default styled.h1.attrs(() => ({}))\` 114 | & > ${'${Title}'} { 115 | color: red; 116 | } 117 | \`; 118 | `); 119 | 120 | expect(code).toMatchSnapshot(); 121 | }); 122 | }); 123 | -------------------------------------------------------------------------------- /packages/interop/babel.config.js: -------------------------------------------------------------------------------- 1 | const config = require('../../babel.config'); 2 | 3 | module.exports = config; 4 | -------------------------------------------------------------------------------- /packages/interop/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@linaria/babel-plugin-interop", 3 | "version": "6.3.0", 4 | "homepage": "https://github.com/callstack/linaria/tree/master/packages/interop#readme", 5 | "repository": "git@github.com:callstack/linaria.git", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Anton Evzhakov", 9 | "email": "anton@evz.name" 10 | }, 11 | "main": "lib/index.js", 12 | "module": "esm/index.js", 13 | "types": "types", 14 | "files": [ 15 | "types/", 16 | "lib/", 17 | "esm/" 18 | ], 19 | "scripts": { 20 | "build": "pnpm build:lib && pnpm build:esm && pnpm build:declarations", 21 | "build:declarations": "tsc --emitDeclarationOnly --outDir types", 22 | "build:esm": "babel src --out-dir esm --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", 23 | "build:lib": "cross-env NODE_ENV=legacy babel src --out-dir lib --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", 24 | "test": "jest --config ../../jest.config.js --rootDir .", 25 | "typecheck": "tsc --noEmit --composite false", 26 | "watch": "pnpm build:lib --watch & pnpm build:esm --watch & pnpm build:declarations --watch" 27 | }, 28 | "devDependencies": { 29 | "@babel/core": "^7.23.5", 30 | "@babel/traverse": "^7.23.5", 31 | "@babel/types": "^7.23.5", 32 | "@types/babel__core": "^7.20.5", 33 | "@types/babel__traverse": "^7.20.4", 34 | "dedent": "^1.5.1" 35 | }, 36 | "engines": { 37 | "node": ">=16.0.0" 38 | }, 39 | "publishConfig": { 40 | "access": "public" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/interop/src/index.ts: -------------------------------------------------------------------------------- 1 | import type core from '@babel/core'; 2 | import type { NodePath } from '@babel/traverse'; 3 | import type { 4 | Expression, 5 | Identifier, 6 | MemberExpression, 7 | TaggedTemplateExpression, 8 | V8IntrinsicIdentifier, 9 | } from '@babel/types'; 10 | 11 | export declare type Core = typeof core; 12 | 13 | export default ( 14 | { types: t }: Core, 15 | config: { library?: string | RegExp } = {} 16 | ) => { 17 | const library = config.library || 'styled-components'; 18 | const isLibrary: (s: string) => boolean = 19 | library instanceof RegExp ? (s) => library.test(s) : (s) => s === library; 20 | 21 | const fixer = (path: NodePath | NodePath) => { 22 | if (!t.isTemplateLiteral(path.parent) || path.listKey !== 'expressions') { 23 | return; 24 | } 25 | 26 | const original = path.node; 27 | path.replaceWithSourceString( 28 | "(i => i && i.__wyw_meta ? '.' + i.__wyw_meta.className : i)('placeholder')" 29 | ); 30 | 31 | const args = path.get('arguments'); 32 | if (Array.isArray(args)) { 33 | args[0]?.replaceWith(original); 34 | } 35 | }; 36 | 37 | const visitors = { 38 | MemberExpression: fixer, 39 | Identifier: fixer, 40 | }; 41 | 42 | return { 43 | visitor: { 44 | TaggedTemplateExpression(path: NodePath) { 45 | const tag = path.get('tag'); 46 | let identifier: NodePath | null = 47 | null; 48 | if (tag.isIdentifier()) { 49 | identifier = tag; 50 | } else if (tag.isMemberExpression()) { 51 | identifier = tag.get('object'); 52 | } else if (tag.isCallExpression()) { 53 | identifier = tag.get('callee'); 54 | } else { 55 | return; 56 | } 57 | 58 | if (identifier.isMemberExpression()) { 59 | const obj = identifier.get('object'); 60 | // it's something like styled().attrs() 61 | if (obj.isCallExpression()) { 62 | identifier = obj.get('callee'); 63 | } else if (obj.isMemberExpression()) { 64 | identifier = obj.get('object'); 65 | } 66 | } 67 | 68 | if (!identifier.isIdentifier()) { 69 | return; 70 | } 71 | 72 | const { scope } = identifier; 73 | const binding = scope.getBinding(identifier.node.name); 74 | const parent = binding?.path.parentPath ?? null; 75 | if (!parent?.isImportDeclaration()) { 76 | return; 77 | } 78 | 79 | const importSource = parent.node.source.value; 80 | if (isLibrary(importSource)) { 81 | path.get('quasi').traverse(visitors); 82 | } 83 | }, 84 | }, 85 | }; 86 | }; 87 | -------------------------------------------------------------------------------- /packages/interop/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { "paths": {}, "rootDir": "src/" }, 4 | "references": [] 5 | } 6 | -------------------------------------------------------------------------------- /packages/linaria/README.md: -------------------------------------------------------------------------------- 1 |

2 | Linaria 3 |

4 | 5 |

6 | Zero-runtime CSS in JS library. 7 |

8 | 9 | --- 10 | 11 | ### 📖 Please refer to the [GitHub](https://github.com/callstack/linaria#readme) for full documentation. 12 | 13 | ## Features 14 | 15 | - Write CSS in JS, but with **zero runtime**, CSS is extracted to CSS files during build 16 | - Familiar **CSS syntax** with Sass like nesting 17 | - Use **dynamic prop based styles** with the React bindings, uses CSS variables behind the scenes 18 | - Easily find where the style was defined with **CSS sourcemaps** 19 | - **Lint your CSS** in JS with [stylelint](https://github.com/stylelint/stylelint) 20 | - Use **JavaScript for logic**, no CSS preprocessor needed 21 | - Optionally use any **CSS preprocessor** such as Sass or PostCSS 22 | 23 | **[Why use Linaria](../../docs/BENEFITS.md)** 24 | 25 | ## Installation 26 | 27 | ```sh 28 | npm install @linaria/core @linaria/react @linaria/babel-preset 29 | ``` 30 | 31 | or 32 | 33 | ```sh 34 | yarn add @linaria/core @linaria/react @linaria/babel-preset 35 | ``` 36 | -------------------------------------------------------------------------------- /packages/linaria/babel.config.js: -------------------------------------------------------------------------------- 1 | const config = require('../../babel.config'); 2 | 3 | module.exports = config; 4 | -------------------------------------------------------------------------------- /packages/linaria/babel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "../lib/babel.js", 3 | "module": "../esm/babel.js", 4 | "types": "../types/babel.d.ts" 5 | } 6 | -------------------------------------------------------------------------------- /packages/linaria/evaluators/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "../lib/evaluators.js", 3 | "module": "../esm/evaluators.js", 4 | "types": "../types/evaluators.d.ts" 5 | } 6 | -------------------------------------------------------------------------------- /packages/linaria/loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "../lib/webpack4-loader.js", 3 | "module": "../esm/webpack4-loader.js", 4 | "types": "../types/webpack4-loader.d.ts" 5 | } 6 | -------------------------------------------------------------------------------- /packages/linaria/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "linaria", 3 | "version": "6.3.0", 4 | "description": "Blazing fast zero-runtime CSS in JS library", 5 | "keywords": [ 6 | "babel", 7 | "babel-plugin", 8 | "css", 9 | "css-in-js", 10 | "linaria", 11 | "react", 12 | "styled-components" 13 | ], 14 | "homepage": "https://github.com/callstack/linaria#readme", 15 | "bugs": "https://github.com/callstack/linaria/issues", 16 | "repository": "git@github.com:callstack/linaria.git", 17 | "license": "MIT", 18 | "main": "lib/core.js", 19 | "module": "esm/core.js", 20 | "types": "types/core.d.ts", 21 | "files": [ 22 | "babel", 23 | "evaluators", 24 | "loader", 25 | "react", 26 | "rollup", 27 | "server", 28 | "stylelint-config", 29 | "types/", 30 | "lib/", 31 | "esm/" 32 | ], 33 | "wyw-in-js": { 34 | "tags": { 35 | "css": "@linaria/core/processors/css", 36 | "styled": "@linaria/react/processors/styled" 37 | } 38 | }, 39 | "scripts": { 40 | "build": "pnpm build:lib && pnpm build:esm && pnpm build:declarations", 41 | "build:declarations": "tsc --emitDeclarationOnly --outDir types", 42 | "build:esm": "babel src --out-dir esm --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", 43 | "build:lib": "cross-env NODE_ENV=legacy babel src --out-dir lib --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", 44 | "typecheck": "tsc --noEmit --composite false", 45 | "watch": "pnpm build:lib --watch & pnpm build:esm --watch & pnpm build:declarations --watch" 46 | }, 47 | "dependencies": { 48 | "@linaria/core": "workspace:^", 49 | "@linaria/react": "workspace:^", 50 | "@linaria/server": "workspace:^" 51 | }, 52 | "engines": { 53 | "node": ">=16.0.0" 54 | }, 55 | "publishConfig": { 56 | "access": "public" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/linaria/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "../lib/react.js", 3 | "module": "../esm/react.js", 4 | "types": "../types/react.d.ts" 5 | } 6 | -------------------------------------------------------------------------------- /packages/linaria/rollup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "../lib/rollup.js", 3 | "module": "../esm/rollup.js", 4 | "types": "../types/rollup.d.ts" 5 | } 6 | -------------------------------------------------------------------------------- /packages/linaria/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "../lib/server.js", 3 | "module": "../esm/server.js", 4 | "types": "../types/server.d.ts" 5 | } 6 | -------------------------------------------------------------------------------- /packages/linaria/src/core.ts: -------------------------------------------------------------------------------- 1 | export * from '@linaria/core'; 2 | -------------------------------------------------------------------------------- /packages/linaria/src/react.ts: -------------------------------------------------------------------------------- 1 | export * from '@linaria/react'; 2 | -------------------------------------------------------------------------------- /packages/linaria/src/server.ts: -------------------------------------------------------------------------------- 1 | export * from '@linaria/server'; 2 | -------------------------------------------------------------------------------- /packages/linaria/stylelint-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "../lib/stylelint.js", 3 | "module": "../esm/stylelint.js", 4 | "types": "../types/stylelint.d.ts" 5 | } 6 | -------------------------------------------------------------------------------- /packages/linaria/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { "paths": {}, "rootDir": "src/" }, 4 | "references": [ 5 | { "path": "../core" }, 6 | { "path": "../react" }, 7 | { "path": "../server" } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/postcss-linaria/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 6.3.0 4 | 5 | ### Minor Changes 6 | 7 | - 281ca4f5: The new version of wyw-in-js, with the support of a configurable code remover, can help prevent compilation errors and improve build time. 8 | 9 | ## 6.2.0 10 | 11 | ### Minor Changes 12 | 13 | - a3dcee2e: Update wyw-in-js to 0.5.3 14 | 15 | ## 6.1.0 16 | 17 | ### Minor Changes 18 | 19 | - 8ba655d3: Bump wyw-in-js to 0.4.0. The full list of changes https://github.com/Anber/wyw-in-js/compare/%40wyw-in-js/transform%400.2.3...%40wyw-in-js/transform%400.4.0 20 | 21 | ## 6.0.0 22 | 23 | ### Major Changes 24 | 25 | - 2ac94b99: BREAKING CHANGE: Linaria has been migrated to wyw-in-js. 26 | 27 | # Migration Guide 28 | 29 | ## For Users 30 | 31 | The main breaking change is that all tooling has been moved from the `@linaria` scope to the `@wyw-in-js` scope. This means that you will need to update your dependencies as follows: 32 | 33 | | Old | New | 34 | | ------------------------ | ------------------------- | 35 | | @linaria/babel-preset | @wyw-in-js/babel-preset | 36 | | @linaria/cli | @wyw-in-js/cli | 37 | | @linaria/esbuild | @wyw-in-js/esbuild | 38 | | @linaria/rollup | @wyw-in-js/rollup | 39 | | @linaria/shaker | discontinued | 40 | | @linaria/vite | @wyw-in-js/vite | 41 | | @linaria/webpack4-loader | discontinued | 42 | | @linaria/webpack5-loader | @wyw-in-js/webpack-loader | 43 | 44 | There is no longer a need to install `@linaria/shaker` as it is now part of `@wyw-in-js/transform`, which will be installed automatically with the bundler plugins. 45 | 46 | The configuration file has been renamed from `linaria.config.js` (`linariarc`) to `wyw-in-js.config.js` (`.wyw-in-jsrc`). 47 | 48 | ## For Custom Processor Developers 49 | 50 | Base classes for processors and most helpers have been moved to `@wyw-in-js/processor-utils`. 51 | 52 | All APIs that had `linaria` in their names have been renamed: 53 | 54 | - The field that stores meta information in runtime has been renamed from `__linaria` to `__wyw_meta` 55 | - The export with all interpolated values has been renamed from `__linariaPreval` to `__wywPreval` 56 | - The caller name in Babel has been renamed from `linaria` to `wyw-in-js` 57 | 58 | For additional information, please visit the [wyw-in-js.dev](https://wyw-in-js.dev). 59 | 60 | ### Patch Changes 61 | 62 | - 63392f9a: Fix the expressions in at-rule parameters and rules with functions. Fixes #1074 63 | 64 | ## 5.0.0 65 | 66 | ### Major Changes 67 | 68 | - 88e07613: Rewritten dependecny tree processing with support for wildcard re-exports. 69 | - cb853e14: All processing stages were merged into one generators-based processor. It allows the implementation of more complex workflows to support features like dynamic imports and re-exports. 70 | 71 | ### Minor Changes 72 | 73 | - 9cb4143d: Refactoring of the 1st stage of transformation. It opens the road to processing wildcard reexports. 74 | 75 | ### Patch Changes 76 | 77 | - 2a1e24a0: Upgrade TypeScript to 5.2 78 | 79 | ## 4.5.1 80 | 81 | ### Patch Changes 82 | 83 | - e59bf809: Shaker mistakenly counts references in types as valuable and keeps referenced variables alive. 84 | 85 | ## 4.5.0 86 | 87 | ### Patch Changes 88 | 89 | - af5bb92d: The end of support for Node.js 14. Migration to pnpm 8. 90 | 91 | ## 4.1.5 92 | 93 | ### Patch Changes 94 | 95 | - 5edde648: Upgrade Babel to support TypeScript 4.9. Fixes #1133. 96 | - e6420897: Update patch version so npm will pick up readme change 97 | 98 | ## 4.1.4 99 | 100 | ### Patch Changes 101 | 102 | - 4c2efaa9: Only lint when file can be parsed by babel, reduce noisey errors during dev 103 | 104 | ## 4.1.3 105 | 106 | ### Patch Changes 107 | 108 | - ce36da42: Add stylelint v14 custom syntax support 109 | -------------------------------------------------------------------------------- /packages/postcss-linaria/README.md: -------------------------------------------------------------------------------- 1 |

2 | Linaria 3 |

4 | 5 |

6 | Zero-runtime CSS in JS library. 7 |

8 | 9 | --- 10 | 11 | 12 | ## Setup 13 | 14 | Please check the Linaria [linting documentation](https://github.com/callstack/linaria/blob/master/docs/LINTING.md) for setup instructions. 15 | 16 | ## Acknowledgements 17 | This project wouldn't have been possible without the following libraries or the people behind them. 18 | 19 | - [postcss-lit](https://github.com/43081j/postcss-lit) (One of the first CSS-in-JS custom syntaxes) 20 | - [stylelint](https://stylelint.io/) 21 | - [postcss](https://postcss.org/) 22 | 23 | ### 📖 Please refer to [GitHub](https://github.com/callstack/linaria#readme) for full documentation. 24 | -------------------------------------------------------------------------------- /packages/postcss-linaria/__tests__/__utils__/index.ts: -------------------------------------------------------------------------------- 1 | import type { Document, Node } from 'postcss'; 2 | 3 | import { parse } from '../../src/parse'; 4 | 5 | export function createTestAst(source: string): { 6 | ast: Document; 7 | source: string; 8 | } { 9 | const ast = parse(source) as Document; 10 | 11 | return { ast, source }; 12 | } 13 | 14 | export function getSourceForNodeByLoc(source: string, node: Node): string { 15 | const loc = node.source; 16 | 17 | if (!loc || !loc.start || !loc.end) { 18 | return ''; 19 | } 20 | 21 | const lines = source.split(/\r\n|\n/); 22 | const result: string[] = []; 23 | const startLineIndex = loc.start.line - 1; 24 | const endLineIndex = loc.end.line - 1; 25 | 26 | for (let i = startLineIndex; i < loc.end.line; i++) { 27 | const line = lines[i]; 28 | if (line) { 29 | let offsetStart = 0; 30 | let offsetEnd = line.length; 31 | 32 | if (i === startLineIndex) { 33 | offsetStart = loc.start.column - 1; 34 | } 35 | 36 | if (i === endLineIndex) { 37 | offsetEnd = loc.end.column; 38 | } 39 | 40 | result.push(line.substring(offsetStart, offsetEnd)); 41 | } 42 | } 43 | 44 | return result.join('\n'); 45 | } 46 | 47 | export function getSourceForNodeByRange(source: string, node: Node): string { 48 | if (!node.source || !node.source.start || !node.source.end) { 49 | return ''; 50 | } 51 | 52 | return source.substring(node.source.start.offset, node.source.end.offset + 1); 53 | } 54 | 55 | export const sourceWithExpression = { 56 | ruleset: ` 57 | const expr = 'color: black'; 58 | css\` 59 | $\{expr} 60 | \`; 61 | `, 62 | singleLineRuleset: ` 63 | css\` 64 | \${expr0} { \${expr1}: \${expr2} } 65 | \` 66 | `, 67 | selectorOrAtRule: ` 68 | const expr = '@media (min-width: 100px)'; 69 | css\` 70 | $\{expr} { 71 | color: black; 72 | } 73 | \`; 74 | `, 75 | selectorBeforeExpression: ` 76 | const expr = '.classname'; 77 | css\` 78 | .example $\{expr} { 79 | color: black; 80 | } 81 | \`; 82 | `, 83 | selectorAfterExpression: ` 84 | const expr = '.classname'; 85 | css\` 86 | $\{expr} .example { 87 | color: black; 88 | } 89 | \`; 90 | `, 91 | declarationProperty: ` 92 | const expr = 'color'; 93 | css\` 94 | \${expr}: black; 95 | \`; 96 | `, 97 | declarationValue: ` 98 | const expr = 'black'; 99 | css\` 100 | color: \${expr}; 101 | \`; 102 | `, 103 | declarationMultipleValues: ` 104 | const expr1 = '10px'; 105 | const expr2 = '5px'; 106 | css\` 107 | margin: \${expr1} \${expr2} \${expr1} \${expr2}; 108 | \`; 109 | `, 110 | declarationMixedValues: ` 111 | const expr1 = '10px'; 112 | const expr2 = '5px'; 113 | css\` 114 | margin: \${expr1} 7px \${expr2} 9px; 115 | \`; 116 | `, 117 | combo: ` 118 | css\` 119 | \${expr0} 120 | .foo { 121 | \${expr1}: \${expr2}; 122 | } 123 | 124 | \${expr3} { 125 | .bar { 126 | color: black; 127 | } 128 | } 129 | \${expr4} 130 | \`; 131 | `, 132 | }; 133 | -------------------------------------------------------------------------------- /packages/postcss-linaria/__tests__/stylelint.test.ts: -------------------------------------------------------------------------------- 1 | import stylelint, { type Config } from 'stylelint'; 2 | 3 | // importing from the package would create circular dependency 4 | // so just import the config directly here 5 | // eslint-disable-next-line import/no-relative-packages 6 | import config from '../../stylelint-config-standard-linaria/src'; 7 | 8 | // note: need to run pnpm install to pick up updates from any parse/stringify changes 9 | describe('stylelint', () => { 10 | it('should not error with valid syntax', async () => { 11 | const source = ` 12 | css\` 13 | \${expr0} 14 | .foo { 15 | \${expr1}: \${expr2}; 16 | } 17 | 18 | \${expr3} { 19 | .bar { 20 | color: black; 21 | } 22 | } 23 | \${expr4} 24 | \`; 25 | `; 26 | const result = await stylelint.lint({ 27 | code: source, 28 | config: config as Config, 29 | }); 30 | 31 | expect(result.errored).toEqual(false); 32 | }); 33 | 34 | it('should be fixable by stylelint', async () => { 35 | const source = ` 36 | css\` 37 | .foo { \${expr1}: \${expr2};; } 38 | \`;`; 39 | const result = await stylelint.lint({ 40 | code: source, 41 | config: { 42 | ...config, 43 | rules: { 44 | ...config.rules, 45 | 'no-extra-semicolons': true, 46 | }, 47 | } as Config, 48 | fix: true, 49 | }); 50 | expect(result.errored).toEqual(false); 51 | expect(result.output).toMatchInlineSnapshot(` 52 | " 53 | css\` 54 | .foo { \${expr1}: \${expr2}; } 55 | \`;" 56 | `); 57 | }); 58 | 59 | it('should be fixable by stylelint with styled api', async () => { 60 | const source = ` 61 | styled.h1\` 62 | .foo { width: \${p => p.size}px;; } 63 | \`;`; 64 | const result = await stylelint.lint({ 65 | code: source, 66 | config: { 67 | ...config, 68 | rules: { 69 | ...config.rules, 70 | 'no-extra-semicolons': true, 71 | }, 72 | } as Config, 73 | fix: true, 74 | }); 75 | expect(result.errored).toEqual(false); 76 | expect(result.output).toMatchInlineSnapshot(` 77 | " 78 | styled.h1\` 79 | .foo { width: \${p => p.size}px; } 80 | \`;" 81 | `); 82 | }); 83 | 84 | it('should be fixable by stylelint with multi-line expressions', async () => { 85 | const source = ` 86 | css\` 87 | $\{expr1} 88 | .foo { \${expr2}: black;; } 89 | \`;`; 90 | const result = await stylelint.lint({ 91 | code: source, 92 | config: { 93 | ...config, 94 | rules: { 95 | ...config.rules, 96 | 'no-extra-semicolons': true, 97 | }, 98 | } as Config, 99 | fix: true, 100 | }); 101 | expect(result.output).toMatchInlineSnapshot(` 102 | " 103 | css\` 104 | $\{expr1} 105 | .foo { \${expr2}: black; } 106 | \`;" 107 | `); 108 | }); 109 | 110 | it('should be compatible with indentation rule', async () => { 111 | const source = ` 112 | css\` 113 | .foo { 114 | width: 100px; 115 | } 116 | \`; 117 | `; 118 | const result = await stylelint.lint({ 119 | code: source, 120 | config: { 121 | ...config, 122 | rules: { 123 | ...config.rules, 124 | indentation: 4, 125 | }, 126 | } as Config, 127 | }); 128 | 129 | expect(result.errored).toEqual(false); 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /packages/postcss-linaria/__tests__/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { createPlaceholder, placeholderText } from '../src/util'; 2 | 3 | describe('createPlaceholder', () => { 4 | it('should create a placeholder for a selector expression', () => { 5 | const expressionCounter = 0; 6 | const sourceAsString = ` 7 | const expr = '@media (min-width: 100px)'; 8 | css\` 9 | $\{expr} { 10 | color: black; 11 | } 12 | \`; 13 | `; 14 | const indexAfterExpression = sourceAsString.indexOf('}') + 1; 15 | const result = createPlaceholder( 16 | expressionCounter, 17 | sourceAsString, 18 | indexAfterExpression 19 | ); 20 | expect(result).toEqual(`.${placeholderText}${expressionCounter}`); 21 | }); 22 | 23 | it('should create a placeholder for a property expression', () => { 24 | const expressionCounter = 0; 25 | const sourceAsString = ` 26 | const expr = 'color'; 27 | css\` 28 | \${expr}: black; 29 | \`; 30 | `; 31 | const indexAfterExpression = sourceAsString.indexOf('}') + 1; 32 | const result = createPlaceholder( 33 | expressionCounter, 34 | sourceAsString, 35 | indexAfterExpression 36 | ); 37 | expect(result).toEqual(`--${placeholderText}${expressionCounter}`); 38 | }); 39 | 40 | it('should create a placeholder for a ruleset expression', () => { 41 | const expressionCounter = 0; 42 | const sourceAsString = ` 43 | const expr = 'color: black'; 44 | css\` 45 | $\{expr} 46 | \`; 47 | `; 48 | const indexAfterExpression = sourceAsString.indexOf('}') + 1; 49 | const result = createPlaceholder( 50 | expressionCounter, 51 | sourceAsString, 52 | indexAfterExpression 53 | ); 54 | expect(result).toEqual(`/* ${placeholderText}:${expressionCounter} */`); 55 | }); 56 | 57 | it('should create a placeholder for a value expression', () => { 58 | const expressionCounter = 0; 59 | const sourceAsString = ` 60 | const expr = 'black'; 61 | css\` 62 | color: \${expr}; 63 | \`; 64 | `; 65 | const indexAfterExpression = sourceAsString.indexOf('}') + 1; 66 | const result = createPlaceholder( 67 | expressionCounter, 68 | sourceAsString, 69 | indexAfterExpression 70 | ); 71 | expect(result).toEqual(`${placeholderText}${expressionCounter}`); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /packages/postcss-linaria/babel.config.js: -------------------------------------------------------------------------------- 1 | const config = require('../../babel.config'); 2 | 3 | module.exports = config; 4 | -------------------------------------------------------------------------------- /packages/postcss-linaria/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@linaria/postcss-linaria", 3 | "version": "6.3.0", 4 | "description": "Blazing fast zero-runtime CSS in JS library", 5 | "keywords": [ 6 | "css", 7 | "css-in-js", 8 | "linaria", 9 | "react", 10 | "styled-components" 11 | ], 12 | "homepage": "https://github.com/callstack/linaria#readme", 13 | "bugs": "https://github.com/callstack/linaria/issues", 14 | "repository": "git@github.com:callstack/linaria.git", 15 | "license": "MIT", 16 | "sideEffects": false, 17 | "main": "lib/index.js", 18 | "module": "esm/index.js", 19 | "types": "types/index.d.ts", 20 | "files": [ 21 | "esm/", 22 | "lib/", 23 | "processors/", 24 | "types/" 25 | ], 26 | "scripts": { 27 | "build": "pnpm build:lib && pnpm build:esm && pnpm build:declarations", 28 | "build:declarations": "tsc --emitDeclarationOnly --outDir types", 29 | "build:esm": "babel src --out-dir esm --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", 30 | "build:lib": "cross-env NODE_ENV=legacy babel src --out-dir lib --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", 31 | "typecheck": "tsc --noEmit --composite false", 32 | "watch": "pnpm build:lib --watch & pnpm build:esm --watch & pnpm build:declarations --watch" 33 | }, 34 | "dependencies": { 35 | "@babel/generator": "^7.23.5", 36 | "@babel/parser": "^7.23.5", 37 | "@babel/traverse": "^7.23.5", 38 | "stylelint": "^14.11.0" 39 | }, 40 | "devDependencies": { 41 | "@babel/types": "^7.23.5", 42 | "@types/babel__generator": "^7.6.7", 43 | "@types/babel__traverse": "^7.20.4", 44 | "postcss": "^8.4.31" 45 | }, 46 | "peerDependencies": { 47 | "postcss": "^8.4.31" 48 | }, 49 | "engines": { 50 | "node": ">=16.0.0" 51 | }, 52 | "publishConfig": { 53 | "access": "public" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/postcss-linaria/src/index.ts: -------------------------------------------------------------------------------- 1 | import { parse } from './parse'; 2 | import { stringify } from './stringify'; 3 | 4 | export { parse, stringify }; 5 | export default { parse, stringify }; 6 | -------------------------------------------------------------------------------- /packages/postcss-linaria/src/util.ts: -------------------------------------------------------------------------------- 1 | const getLine = (sourceAsString: string, indexAfterExpression: number) => { 2 | const begginningOfLineIndex = 3 | sourceAsString.lastIndexOf('\n', indexAfterExpression) || 0; 4 | const endOfLineIndex = 5 | sourceAsString.indexOf('\n', indexAfterExpression - 1) || Infinity; 6 | const indexAfterExpressionInLine = 7 | indexAfterExpression - begginningOfLineIndex; 8 | return { 9 | line: sourceAsString.substring(begginningOfLineIndex, endOfLineIndex + 1), 10 | indexAfterExpressionInLine, 11 | }; 12 | }; 13 | 14 | const isSelector = (sourceAsString: string, indexAfterExpression: number) => { 15 | const { line } = getLine(sourceAsString, indexAfterExpression); 16 | const isSingleLineRule = 17 | line.indexOf('{', indexAfterExpression) > 0 && 18 | line.indexOf('}', indexAfterExpression) > 0; 19 | return isSingleLineRule || line[line.length - 2] === '{'; 20 | }; 21 | 22 | const isProperty = (sourceAsString: string, indexAfterExpression: number) => { 23 | return sourceAsString[indexAfterExpression] === ':'; 24 | }; 25 | 26 | // no ':' or '{' on the line 27 | const isRuleSet = (sourceAsString: string, indexAfterExpression: number) => { 28 | const { line: possibleRuleset, indexAfterExpressionInLine } = getLine( 29 | sourceAsString, 30 | indexAfterExpression 31 | ); 32 | const hasCurlyBraceAfterExpression = 33 | possibleRuleset.indexOf('{', indexAfterExpressionInLine) > 0; 34 | const hasCommmaAfterExpression = 35 | possibleRuleset.indexOf(',', indexAfterExpressionInLine) > 0; 36 | 37 | // check if possible ruleset has ':' and outside of the func args if expression has a func 38 | // i.e. avoid false postivive for `${func({ key: value })} 39 | const indexOfOpenParenthesis = possibleRuleset.indexOf('('); 40 | const indexOfClosedParenthesis = possibleRuleset.indexOf(')'); 41 | const hasFuncInExpression = 42 | indexOfOpenParenthesis > 0 && indexOfClosedParenthesis > 0; 43 | let hasColonOutsideOfExpression = possibleRuleset.includes(':'); 44 | if (hasFuncInExpression) { 45 | hasColonOutsideOfExpression = 46 | possibleRuleset.lastIndexOf(':', indexOfOpenParenthesis) > 0 || 47 | possibleRuleset.indexOf(':', indexOfClosedParenthesis) > 0; 48 | } 49 | 50 | return !( 51 | hasColonOutsideOfExpression || 52 | hasCurlyBraceAfterExpression || 53 | hasCommmaAfterExpression 54 | ); 55 | }; 56 | 57 | export const placeholderText = 'pcss_lin'; 58 | 59 | export const createPlaceholder = ( 60 | i: number, 61 | sourceAsString: string, 62 | indexAfterExpression: number 63 | ): string => { 64 | if (isSelector(sourceAsString, indexAfterExpression)) { 65 | return `.${placeholderText}${i}`; 66 | } 67 | if (isProperty(sourceAsString, indexAfterExpression)) { 68 | return `--${placeholderText}${i}`; 69 | } 70 | if (isRuleSet(sourceAsString, indexAfterExpression)) { 71 | return `/* ${placeholderText}:${i} */`; 72 | } 73 | 74 | // assume it's a property value or part of another string; 75 | return `${placeholderText}${i}`; 76 | }; 77 | -------------------------------------------------------------------------------- /packages/postcss-linaria/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { "paths": {}, "rootDir": "src/" }, 4 | "references": [] 5 | } 6 | -------------------------------------------------------------------------------- /packages/react/README.md: -------------------------------------------------------------------------------- 1 |

2 | Linaria 3 |

4 | 5 |

6 | Zero-runtime CSS in JS library. 7 |

8 | 9 | --- 10 | 11 | ### 📖 Please refer to the [GitHub](https://github.com/callstack/linaria#readme) for full documentation. 12 | 13 | ## Features 14 | 15 | - Write CSS in JS, but with **zero runtime**, CSS is extracted to CSS files during build 16 | - Familiar **CSS syntax** with Sass like nesting 17 | - Use **dynamic prop based styles** with the React bindings, uses CSS variables behind the scenes 18 | - Easily find where the style was defined with **CSS sourcemaps** 19 | - **Lint your CSS** in JS with [stylelint](https://github.com/stylelint/stylelint) 20 | - Use **JavaScript for logic**, no CSS preprocessor needed 21 | - Optionally use any **CSS preprocessor** such as Sass or PostCSS 22 | 23 | **[Why use Linaria](../../docs/BENEFITS.md)** 24 | 25 | ## Installation 26 | 27 | ```sh 28 | npm install @linaria/core @linaria/react @linaria/babel-preset 29 | ``` 30 | 31 | or 32 | 33 | ```sh 34 | yarn add @linaria/core @linaria/react @linaria/babel-preset 35 | ``` 36 | -------------------------------------------------------------------------------- /packages/react/__dtslint__/index.d.ts: -------------------------------------------------------------------------------- 1 | // dtslint wants to see index.d.ts. Well, here it is. 2 | declare const linaria: unknown; 3 | -------------------------------------------------------------------------------- /packages/react/__dtslint__/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react/__dtslint__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "lib": ["es6"], 5 | "target": "ES2015", 6 | "esModuleInterop": true, 7 | "noImplicitAny": true, 8 | "noImplicitThis": true, 9 | "strictNullChecks": true, 10 | "strictFunctionTypes": true, 11 | "noEmit": true, 12 | 13 | "baseUrl": "./" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/react/__tests__/__snapshots__/styled.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`applies CSS variables in style prop 1`] = ` 4 |
15 | This is a test 16 |
17 | `; 18 | 19 | exports[`does not filter attributes for components 1`] = ` 20 |
21 | voila 22 |
23 | `; 24 | 25 | exports[`does not filter attributes for kebab cased custom elements 1`] = ` 26 | 30 | This is a test 31 | 32 | `; 33 | 34 | exports[`does not filter attributes for unknown tag 1`] = ` 35 | 39 | This is a test 40 | 41 | `; 42 | 43 | exports[`does not filter attributes for upper camel cased custom elements 1`] = ` 44 | 48 | This is a test 49 | 50 | `; 51 | 52 | exports[`filters unknown html attributes for HTML tag 1`] = ` 53 |
56 | This is a test 57 |
58 | `; 59 | 60 | exports[`forwards as prop when wrapping another styled component 1`] = ` 61 | 64 | This is a test 65 | 66 | `; 67 | 68 | exports[`handles wrapping another styled component 1`] = ` 69 |
72 | This is a test 73 |
74 | `; 75 | 76 | exports[`merges CSS variables with custom style prop 1`] = ` 77 |
86 | This is a test 87 |
88 | `; 89 | 90 | exports[`provides linaria component className for composition as last item in props.className 1`] = ` 91 |
94 | original classname used for composition 95 |
96 | `; 97 | 98 | exports[`renders component with display name and class name 1`] = ` 99 |
102 | This is a test 103 |
104 | `; 105 | 106 | exports[`renders tag with display name and class name 1`] = ` 107 |

110 | This is a test 111 |

112 | `; 113 | 114 | exports[`replaces custom component with as prop for primitive 1`] = ` 115 | 119 | This is a test 120 | 121 | `; 122 | 123 | exports[`replaces primitive with as prop for custom component 1`] = ` 124 |
134 | This is a test 135 |
136 | `; 137 | 138 | exports[`replaces simple component with as prop 1`] = ` 139 | 143 | This is a test 144 | 145 | `; 146 | 147 | exports[`supports extra class prop 1`] = ` 148 |
151 | This is a test 152 |
153 | `; 154 | 155 | exports[`supports extra className prop 1`] = ` 156 |
159 | This is a test 160 |
161 | `; 162 | -------------------------------------------------------------------------------- /packages/react/__tests__/detect-core-js.test.ts: -------------------------------------------------------------------------------- 1 | import cp from 'child_process'; 2 | 3 | const waitForProcess = async (process) => { 4 | return new Promise((resolve) => { 5 | let output = ''; 6 | process.stdout.on('data', (chunk) => { 7 | output += chunk.toString(); 8 | }); 9 | process.on('close', () => { 10 | resolve(output); 11 | }); 12 | }); 13 | }; 14 | 15 | it('Ensures that package do not include core-js dependency after build', async () => { 16 | // eslint-disable-next-line import/no-extraneous-dependencies 17 | const packageJSON = require('@linaria/react/package.json'); 18 | const buildScript = packageJSON.scripts['build:corejs-test']; 19 | 20 | const proc = cp.exec(buildScript, { 21 | stdio: 'ignore', 22 | env: { 23 | ...process.env, 24 | DEBUG_CORE_JS: 'true', 25 | }, 26 | }); 27 | const result = await waitForProcess(proc); 28 | // run `DEBUG_CORE_JS=true yarn build:lib` to debug issues with introduced core-js dependency 29 | expect(result).not.toContain( 30 | 'The corejs3 polyfill added the following polyfills' 31 | ); 32 | expect(result).toContain( 33 | 'Based on your code and targets, the corejs3 polyfill did not add any polyfill' 34 | ); 35 | }, 15000); 36 | -------------------------------------------------------------------------------- /packages/react/babel.config.js: -------------------------------------------------------------------------------- 1 | const config = require('../../babel.config'); 2 | 3 | module.exports = config; 4 | -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@linaria/react", 3 | "version": "6.3.0", 4 | "description": "Blazing fast zero-runtime CSS in JS library", 5 | "keywords": [ 6 | "css", 7 | "css-in-js", 8 | "linaria", 9 | "react", 10 | "styled-components" 11 | ], 12 | "homepage": "https://github.com/callstack/linaria#readme", 13 | "bugs": "https://github.com/callstack/linaria/issues", 14 | "repository": "git@github.com:callstack/linaria.git", 15 | "license": "MIT", 16 | "sideEffects": false, 17 | "exports": { 18 | "./package.json": "./package.json", 19 | ".": { 20 | "types": "./types/index.d.ts", 21 | "import": "./dist/index.mjs", 22 | "default": "./dist/index.js" 23 | }, 24 | "./*": { 25 | "types": "./types/*.d.ts", 26 | "import": "./dist/*.mjs", 27 | "default": "./dist/*.js" 28 | } 29 | }, 30 | "main": "dist/index.js", 31 | "module": "dist/index.mjs", 32 | "types": "types/index.d.ts", 33 | "typesVersions": { 34 | "*": { 35 | "processors/*": [ 36 | "./types/processors/*.d.ts" 37 | ] 38 | } 39 | }, 40 | "files": [ 41 | "dist/", 42 | "processors/", 43 | "types/" 44 | ], 45 | "wyw-in-js": { 46 | "tags": { 47 | "styled": "./dist/processors/styled.js" 48 | } 49 | }, 50 | "scripts": { 51 | "build": "pnpm build:dist && pnpm build:declarations", 52 | "build:corejs-test": "cross-env NODE_ENV=legacy babel src --out-dir lib --extensions '.js,.jsx,.ts,.tsx' --ignore \"src/processors/**/*\"", 53 | "build:declarations": "tsc --emitDeclarationOnly --outDir types", 54 | "build:dist": "tsup --format cjs,esm", 55 | "test": "jest --config ../../jest.config.js --rootDir .", 56 | "test:dts": "dtslint --localTs ../../node_modules/typescript/lib __dtslint__", 57 | "typecheck": "tsc --noEmit --composite false", 58 | "watch": "pnpm build:dist --watch & pnpm build:declarations --watch" 59 | }, 60 | "dependencies": { 61 | "@emotion/is-prop-valid": "^1.2.0", 62 | "@linaria/core": "workspace:^", 63 | "@wyw-in-js/processor-utils": "^0.6.0", 64 | "@wyw-in-js/shared": "^0.6.0", 65 | "minimatch": "^9.0.3", 66 | "react-html-attributes": "^1.4.6", 67 | "resolve": "^1.22.8", 68 | "ts-invariant": "^0.10.3" 69 | }, 70 | "devDependencies": { 71 | "@babel/types": "^7.23.5", 72 | "@types/babel__core": "^7.20.5", 73 | "@types/node": "^17.0.39", 74 | "@types/react": ">=16", 75 | "react": "^16.14.0", 76 | "react-test-renderer": "^16.8.3" 77 | }, 78 | "peerDependencies": { 79 | "react": ">=16" 80 | }, 81 | "engines": { 82 | "node": ">=16.0.0" 83 | }, 84 | "publishConfig": { 85 | "access": "public" 86 | }, 87 | "tsup": { 88 | "entry": [ 89 | "src/index.ts", 90 | "src/processors/styled.ts" 91 | ], 92 | "splitting": false, 93 | "sourcemap": true, 94 | "clean": true 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /packages/react/processors/styled.js: -------------------------------------------------------------------------------- 1 | Object.defineProperty(exports, '__esModule', { 2 | value: true, 3 | }); 4 | 5 | exports.default = require('../dist/processors/styled').default; 6 | -------------------------------------------------------------------------------- /packages/react/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as styled } from './styled'; 2 | export type { 3 | HtmlStyledTag, 4 | StyledComponent, 5 | StyledJSXIntrinsics, 6 | Styled, 7 | } from './styled'; 8 | export type { CSSProperties } from '@linaria/core'; 9 | export type { WYWEvalMeta } from '@wyw-in-js/shared'; 10 | -------------------------------------------------------------------------------- /packages/react/src/react-html-attributes.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'react-html-attributes' { 2 | interface IElements { 3 | html: string[]; 4 | svg: string[]; 5 | } 6 | 7 | interface IAttributes { 8 | [tag: string]: string[]; 9 | } 10 | 11 | const result: IAttributes & { 12 | elements: IElements; 13 | }; 14 | 15 | export = result; 16 | } 17 | -------------------------------------------------------------------------------- /packages/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "paths": {}, 5 | "rootDir": "src/", 6 | "types": [ 7 | "node" 8 | ], 9 | }, 10 | "references": [{ "path": "../core" }] 11 | } 12 | -------------------------------------------------------------------------------- /packages/server/README.md: -------------------------------------------------------------------------------- 1 |

2 | Linaria 3 |

4 | 5 |

6 | Zero-runtime CSS in JS library. 7 |

8 | 9 | --- 10 | 11 | ### 📖 Please refer to the [GitHub](https://github.com/callstack/linaria#readme) for full documentation. 12 | 13 | ## Features 14 | 15 | - Write CSS in JS, but with **zero runtime**, CSS is extracted to CSS files during build 16 | - Familiar **CSS syntax** with Sass like nesting 17 | - Use **dynamic prop based styles** with the React bindings, uses CSS variables behind the scenes 18 | - Easily find where the style was defined with **CSS sourcemaps** 19 | - **Lint your CSS** in JS with [stylelint](https://github.com/stylelint/stylelint) 20 | - Use **JavaScript for logic**, no CSS preprocessor needed 21 | - Optionally use any **CSS preprocessor** such as Sass or PostCSS 22 | 23 | **[Why use Linaria](../../docs/BENEFITS.md)** 24 | 25 | ## Installation 26 | 27 | ```sh 28 | npm install @linaria/core @linaria/react @linaria/babel-preset 29 | ``` 30 | 31 | or 32 | 33 | ```sh 34 | yarn add @linaria/core @linaria/react @linaria/babel-preset 35 | ``` 36 | -------------------------------------------------------------------------------- /packages/server/babel.config.js: -------------------------------------------------------------------------------- 1 | const config = require('../../babel.config'); 2 | 3 | module.exports = config; 4 | -------------------------------------------------------------------------------- /packages/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@linaria/server", 3 | "version": "6.3.0", 4 | "description": "Blazing fast zero-runtime CSS in JS library", 5 | "keywords": [ 6 | "css", 7 | "css-in-js", 8 | "linaria", 9 | "react", 10 | "styled-components" 11 | ], 12 | "homepage": "https://github.com/callstack/linaria#readme", 13 | "bugs": "https://github.com/callstack/linaria/issues", 14 | "repository": "git@github.com:callstack/linaria.git", 15 | "license": "MIT", 16 | "main": "lib/index.js", 17 | "module": "esm/index.js", 18 | "types": "types", 19 | "files": [ 20 | "types/", 21 | "lib/", 22 | "esm/" 23 | ], 24 | "scripts": { 25 | "build": "pnpm build:lib && pnpm build:esm && pnpm build:declarations", 26 | "build:declarations": "tsc --emitDeclarationOnly --outDir types", 27 | "build:esm": "babel src --out-dir esm --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", 28 | "build:lib": "cross-env NODE_ENV=legacy babel src --out-dir lib --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", 29 | "typecheck": "tsc --noEmit --composite false", 30 | "watch": "pnpm build:lib --watch & pnpm build:esm --watch & pnpm build:declarations --watch" 31 | }, 32 | "dependencies": { 33 | "postcss": "^8.4.31" 34 | }, 35 | "devDependencies": { 36 | "dedent": "^1.5.1", 37 | "prettier": "^3.0.3" 38 | }, 39 | "engines": { 40 | "node": ">=16.0.0" 41 | }, 42 | "publishConfig": { 43 | "access": "public" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/server/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as collect } from './collect'; 2 | -------------------------------------------------------------------------------- /packages/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "paths": {}, 5 | "rootDir": "src/" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/stylelint-config-standard-linaria/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 6.3.0 4 | 5 | ### Minor Changes 6 | 7 | - 281ca4f5: The new version of wyw-in-js, with the support of a configurable code remover, can help prevent compilation errors and improve build time. 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies [281ca4f5] 12 | - @linaria/postcss-linaria@6.3.0 13 | 14 | ## 6.2.0 15 | 16 | ### Minor Changes 17 | 18 | - a3dcee2e: Update wyw-in-js to 0.5.3 19 | 20 | ### Patch Changes 21 | 22 | - Updated dependencies [a3dcee2e] 23 | - @linaria/postcss-linaria@6.2.0 24 | 25 | ## 6.1.0 26 | 27 | ### Minor Changes 28 | 29 | - 8ba655d3: Bump wyw-in-js to 0.4.0. The full list of changes https://github.com/Anber/wyw-in-js/compare/%40wyw-in-js/transform%400.2.3...%40wyw-in-js/transform%400.4.0 30 | 31 | ### Patch Changes 32 | 33 | - Updated dependencies [8ba655d3] 34 | - @linaria/postcss-linaria@6.1.0 35 | 36 | ## 6.0.0 37 | 38 | ### Major Changes 39 | 40 | - 2ac94b99: BREAKING CHANGE: Linaria has been migrated to wyw-in-js. 41 | 42 | # Migration Guide 43 | 44 | ## For Users 45 | 46 | The main breaking change is that all tooling has been moved from the `@linaria` scope to the `@wyw-in-js` scope. This means that you will need to update your dependencies as follows: 47 | 48 | | Old | New | 49 | | ------------------------ | ------------------------- | 50 | | @linaria/babel-preset | @wyw-in-js/babel-preset | 51 | | @linaria/cli | @wyw-in-js/cli | 52 | | @linaria/esbuild | @wyw-in-js/esbuild | 53 | | @linaria/rollup | @wyw-in-js/rollup | 54 | | @linaria/shaker | discontinued | 55 | | @linaria/vite | @wyw-in-js/vite | 56 | | @linaria/webpack4-loader | discontinued | 57 | | @linaria/webpack5-loader | @wyw-in-js/webpack-loader | 58 | 59 | There is no longer a need to install `@linaria/shaker` as it is now part of `@wyw-in-js/transform`, which will be installed automatically with the bundler plugins. 60 | 61 | The configuration file has been renamed from `linaria.config.js` (`linariarc`) to `wyw-in-js.config.js` (`.wyw-in-jsrc`). 62 | 63 | ## For Custom Processor Developers 64 | 65 | Base classes for processors and most helpers have been moved to `@wyw-in-js/processor-utils`. 66 | 67 | All APIs that had `linaria` in their names have been renamed: 68 | 69 | - The field that stores meta information in runtime has been renamed from `__linaria` to `__wyw_meta` 70 | - The export with all interpolated values has been renamed from `__linariaPreval` to `__wywPreval` 71 | - The caller name in Babel has been renamed from `linaria` to `wyw-in-js` 72 | 73 | For additional information, please visit the [wyw-in-js.dev](https://wyw-in-js.dev). 74 | 75 | ### Patch Changes 76 | 77 | - Updated dependencies [63392f9a] 78 | - Updated dependencies [2ac94b99] 79 | - @linaria/postcss-linaria@6.0.0 80 | 81 | ## 5.0.0 82 | 83 | ### Major Changes 84 | 85 | - 88e07613: Rewritten dependecny tree processing with support for wildcard re-exports. 86 | - cb853e14: All processing stages were merged into one generators-based processor. It allows the implementation of more complex workflows to support features like dynamic imports and re-exports. 87 | 88 | ### Minor Changes 89 | 90 | - 9cb4143d: Refactoring of the 1st stage of transformation. It opens the road to processing wildcard reexports. 91 | 92 | ### Patch Changes 93 | 94 | - 2a1e24a0: Upgrade TypeScript to 5.2 95 | - Updated dependencies [9cb4143d] 96 | - Updated dependencies [88e07613] 97 | - Updated dependencies [2a1e24a0] 98 | - Updated dependencies [cb853e14] 99 | - @linaria/postcss-linaria@5.0.0 100 | 101 | ## 4.5.1 102 | 103 | ### Patch Changes 104 | 105 | - Updated dependencies [e59bf809] 106 | - @linaria/postcss-linaria@4.5.1 107 | 108 | ## 4.5.0 109 | 110 | ### Patch Changes 111 | 112 | - af5bb92d: The end of support for Node.js 14. Migration to pnpm 8. 113 | - Updated dependencies [16c057df] 114 | - Updated dependencies [af5bb92d] 115 | - @linaria/postcss-linaria@4.5.0 116 | 117 | ## 4.1.5 118 | 119 | ### Patch Changes 120 | 121 | - e6420897: Update patch version so npm will pick up readme change 122 | - Updated dependencies [5edde648] 123 | - Updated dependencies [e6420897] 124 | - @linaria/postcss-linaria@4.1.5 125 | 126 | ## 4.1.4 127 | 128 | ### Patch Changes 129 | 130 | - Updated dependencies [4c2efaa9] 131 | - @linaria/postcss-linaria@4.1.4 132 | 133 | ## 4.1.3 134 | 135 | ### Patch Changes 136 | 137 | - ce36da42: Add stylelint v14 custom syntax support 138 | - Updated dependencies [ce36da42] 139 | - @linaria/postcss-linaria@4.1.3 140 | -------------------------------------------------------------------------------- /packages/stylelint-config-standard-linaria/README.md: -------------------------------------------------------------------------------- 1 |

2 | Linaria 3 |

4 | 5 |

6 | Zero-runtime CSS in JS library. 7 |

8 | 9 | --- 10 | 11 | 12 | ## Setup 13 | 14 | Please check the Linaria [linting documentation](https://github.com/callstack/linaria/blob/master/docs/LINTING.md) for setup instructions. 15 | 16 | ## Acknowledgements 17 | This project wouldn't have been possible without the following libraries or the people behind them. 18 | 19 | - [postcss-lit](https://github.com/43081j/postcss-lit) (One of the first CSS-in-JS custom syntaxes) 20 | - [stylelint](https://stylelint.io/) 21 | - [postcss](https://postcss.org/) 22 | 23 | ### 📖 Please refer to [GitHub](https://github.com/callstack/linaria#readme) for full documentation. 24 | -------------------------------------------------------------------------------- /packages/stylelint-config-standard-linaria/babel.config.js: -------------------------------------------------------------------------------- 1 | const config = require('../../babel.config'); 2 | 3 | module.exports = config; 4 | -------------------------------------------------------------------------------- /packages/stylelint-config-standard-linaria/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@linaria/stylelint-config-standard-linaria", 3 | "version": "6.3.0", 4 | "description": "Blazing fast zero-runtime CSS in JS library", 5 | "keywords": [ 6 | "css", 7 | "css-in-js", 8 | "linaria", 9 | "react", 10 | "styled-components" 11 | ], 12 | "homepage": "https://github.com/callstack/linaria#readme", 13 | "bugs": "https://github.com/callstack/linaria/issues", 14 | "repository": "git@github.com:callstack/linaria.git", 15 | "license": "MIT", 16 | "sideEffects": false, 17 | "main": "lib/index.js", 18 | "module": "esm/index.js", 19 | "types": "types/index.d.ts", 20 | "files": [ 21 | "esm/", 22 | "lib/", 23 | "processors/", 24 | "types/" 25 | ], 26 | "scripts": { 27 | "build": "pnpm build:lib && pnpm build:esm && pnpm build:declarations", 28 | "build:declarations": "tsc --emitDeclarationOnly --outDir types", 29 | "build:esm": "babel src --out-dir esm --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", 30 | "build:lib": "cross-env NODE_ENV=legacy babel src --out-dir lib --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", 31 | "typecheck": "tsc --noEmit --composite false", 32 | "watch": "pnpm build:lib --watch & pnpm build:esm --watch & pnpm build:declarations --watch" 33 | }, 34 | "dependencies": { 35 | "@linaria/postcss-linaria": "workspace:^", 36 | "stylelint": "^14.11.0", 37 | "stylelint-config-standard": "^28.0.0" 38 | }, 39 | "engines": { 40 | "node": ">=16.0.0" 41 | }, 42 | "publishConfig": { 43 | "access": "public" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/stylelint-config-standard-linaria/src/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['stylelint-config-standard'], 3 | // eslint-disable-next-line global-require 4 | customSyntax: require('@linaria/postcss-linaria'), 5 | rules: { 6 | 'property-no-vendor-prefix': true, 7 | 'string-no-newline': true, 8 | 'value-no-vendor-prefix': true, 9 | 'no-empty-source': null, 10 | 'no-extra-semicolons': null, 11 | // /* pcss-lin */ placeholder comments are added during parsing 12 | 'comment-empty-line-before': [ 13 | 'always', 14 | { 15 | except: ['first-nested'], 16 | ignore: ['stylelint-commands'], 17 | ignoreComments: [/pcss-lin/], 18 | }, 19 | ], 20 | // '//' comments create unknown word issues while linting. Force using /* */ 21 | 'no-invalid-double-slash-comments': true, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /packages/stylelint-config-standard-linaria/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { "paths": {}, "rootDir": "src/" }, 4 | "references": [] 5 | } 6 | -------------------------------------------------------------------------------- /packages/stylelint/README.md: -------------------------------------------------------------------------------- 1 |

2 | Linaria 3 |

4 | 5 |

6 | Zero-runtime CSS in JS library. 7 |

8 | 9 | --- 10 | 11 | ## Setup 12 | 13 | Please check the Linaria [linting documentation](https://github.com/callstack/linaria/blob/master/docs/LINTING.md) for setup instructions. 14 | 15 | ### 📖 Please refer to the [GitHub](https://github.com/callstack/linaria#readme) for full documentation. 16 | -------------------------------------------------------------------------------- /packages/stylelint/babel.config.js: -------------------------------------------------------------------------------- 1 | const config = require('../../babel.config'); 2 | 3 | module.exports = config; 4 | -------------------------------------------------------------------------------- /packages/stylelint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@linaria/stylelint", 3 | "version": "6.3.0", 4 | "description": "Blazing fast zero-runtime CSS in JS library", 5 | "keywords": [ 6 | "css", 7 | "css-in-js", 8 | "linaria", 9 | "react", 10 | "styled-components" 11 | ], 12 | "homepage": "https://github.com/callstack/linaria#readme", 13 | "bugs": "https://github.com/callstack/linaria/issues", 14 | "repository": "git@github.com:callstack/linaria.git", 15 | "license": "MIT", 16 | "main": "lib/index.js", 17 | "module": "esm/index.js", 18 | "types": "types", 19 | "files": [ 20 | "types/", 21 | "lib/", 22 | "esm/" 23 | ], 24 | "scripts": { 25 | "build": "pnpm build:lib && pnpm build:esm && pnpm build:declarations", 26 | "build:declarations": "tsc --emitDeclarationOnly --outDir types", 27 | "build:esm": "babel src --out-dir esm --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", 28 | "build:lib": "cross-env NODE_ENV=legacy babel src --out-dir lib --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", 29 | "typecheck": "tsc --noEmit --composite false", 30 | "watch": "pnpm build:lib --watch & pnpm build:esm --watch & pnpm build:declarations --watch" 31 | }, 32 | "dependencies": { 33 | "@wyw-in-js/shared": "^0.6.0", 34 | "@wyw-in-js/transform": "^0.6.0" 35 | }, 36 | "devDependencies": { 37 | "@types/node": "^17.0.39" 38 | }, 39 | "engines": { 40 | "node": ">=16.11.0" 41 | }, 42 | "publishConfig": { 43 | "access": "public" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/stylelint/src/index.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | processors: [require.resolve('./preprocessor')], 3 | syntax: 'scss', 4 | rules: { 5 | 'property-no-vendor-prefix': true, 6 | 'string-no-newline': true, 7 | 'value-no-vendor-prefix': true, 8 | 'no-empty-source': null, 9 | 'no-extra-semicolons': null, 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/stylelint/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { "paths": {}, "rootDir": "src/", "types": ["node"] }, 4 | "references": [] 5 | } 6 | -------------------------------------------------------------------------------- /packages/testkit/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | module.exports = { 3 | preset: 'ts-jest', 4 | testEnvironment: 'node', 5 | }; 6 | -------------------------------------------------------------------------------- /packages/testkit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@linaria/testkit", 3 | "version": "6.3.0", 4 | "private": true, 5 | "description": "Blazing fast zero-runtime CSS in JS library", 6 | "keywords": [ 7 | "babel", 8 | "babel-plugin", 9 | "css", 10 | "css-in-js", 11 | "linaria", 12 | "react", 13 | "styled-components" 14 | ], 15 | "homepage": "https://github.com/callstack/linaria#readme", 16 | "bugs": "https://github.com/callstack/linaria/issues", 17 | "repository": "git@github.com:callstack/linaria.git", 18 | "license": "MIT", 19 | "scripts": { 20 | "test": "jest --config ./jest.config.js --rootDir .", 21 | "typecheck": "tsc --noEmit --composite false" 22 | }, 23 | "dependencies": { 24 | "@babel/core": "^7.23.5", 25 | "@babel/generator": "^7.23.5", 26 | "@babel/traverse": "^7.23.5", 27 | "@linaria/react": "workspace:^", 28 | "@swc/core": "^1.3.20", 29 | "@wyw-in-js/processor-utils": "^0.6.0", 30 | "@wyw-in-js/shared": "^0.6.0", 31 | "@wyw-in-js/transform": "^0.6.0", 32 | "debug": "^4.3.4", 33 | "dedent": "^1.5.1", 34 | "esbuild": "^0.15.16", 35 | "typescript": "^5.2.2" 36 | }, 37 | "devDependencies": { 38 | "@babel/plugin-syntax-jsx": "^7.23.3", 39 | "@babel/plugin-syntax-typescript": "^7.23.3", 40 | "@babel/plugin-transform-modules-commonjs": "^7.23.3", 41 | "@babel/preset-typescript": "^7.23.3", 42 | "@babel/runtime": "^7.23.5", 43 | "@babel/types": "^7.23.5", 44 | "@linaria/atomic": "workspace:^", 45 | "@linaria/core": "workspace:^", 46 | "@types/babel__core": "^7.20.5", 47 | "@types/babel__generator": "^7.6.7", 48 | "@types/babel__traverse": "^7.20.4", 49 | "@types/debug": "^4.1.8", 50 | "@types/jest": "^28.1.0", 51 | "@types/node": "^17.0.39", 52 | "babel-plugin-istanbul": "^6.1.1", 53 | "babel-plugin-module-resolver": "^4.1.0", 54 | "jest": "^29.6.2", 55 | "linaria": "workspace:^", 56 | "ts-jest": "^29.1.1" 57 | }, 58 | "engines": { 59 | "node": ">=16.11.0" 60 | }, 61 | "publishConfig": { 62 | "access": "public" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/assignToExport.js: -------------------------------------------------------------------------------- 1 | var Padding = (exports.Padding = 4); 2 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/bar.js: -------------------------------------------------------------------------------- 1 | export * from './re-exports/constants'; 2 | 3 | export const bar1 = 'bar1'; 4 | export const bar2 = 'bar2'; 5 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/circular-imports/bar.js: -------------------------------------------------------------------------------- 1 | export const bar = 'bar'; 2 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/circular-imports/constants.js: -------------------------------------------------------------------------------- 1 | import { bar } from './index'; 2 | 3 | export const foo = 'foo'; 4 | export const constBar = bar; 5 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/circular-imports/foo.js: -------------------------------------------------------------------------------- 1 | import * as fooStyles from './constants'; 2 | 3 | export { fooStyles }; 4 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/circular-imports/index.js: -------------------------------------------------------------------------------- 1 | export * from './bar'; 2 | export * from './foo'; 3 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/complex-component.js: -------------------------------------------------------------------------------- 1 | // Dead code in this file should be ignored 2 | 3 | import deadDep from 'unknown-dependency'; 4 | import { styled } from '@linaria/react'; 5 | 6 | export const deadValue = deadDep(); 7 | 8 | const objects = { font: { fontSize: 12 }, box: { border: '1px solid red' } }; 9 | const foo = (k) => { 10 | const { [k]: obj } = objects; 11 | return obj; 12 | }; 13 | 14 | objects.font.fontWeight = 'bold'; 15 | 16 | export const whiteColor = '#fff'; 17 | 18 | export const Title = styled.h1` 19 | ${foo('font')} 20 | ${foo('box')} 21 | `; 22 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/components-library.js: -------------------------------------------------------------------------------- 1 | import { styled } from '@linaria/react'; 2 | 3 | export const T1 = styled.h1` 4 | background: #111; 5 | `; 6 | export const T2 = styled.h2` 7 | background: #222; 8 | `; 9 | export const T3 = styled.h3` 10 | ${T2} { 11 | background: #333; 12 | } 13 | `; 14 | export default styled.p` 15 | background: #333; 16 | `; 17 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/computedKeys.js: -------------------------------------------------------------------------------- 1 | const computedKeyName = 'red'; 2 | export const object = { 3 | [computedKeyName]: 'red', 4 | blue: 'blue', 5 | }; 6 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/enums.ts: -------------------------------------------------------------------------------- 1 | export enum Colors { 2 | BLUE = '#27509A', 3 | } 4 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/escape-character.js: -------------------------------------------------------------------------------- 1 | import { styled } from '@linaria/react'; 2 | 3 | const selectors = ['a', 'b']; 4 | 5 | export const Block = styled.div` 6 | ${selectors.map((c) => String.raw`${c} { content: "\u000A"; }`).join('\n')}; 7 | `; 8 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/foo.js: -------------------------------------------------------------------------------- 1 | export const foo1 = 'foo1'; 2 | export const foo2 = 'foo2'; 3 | export const foo3 = () => 'foo3'; 4 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/linaria-ui-library/components/index.js: -------------------------------------------------------------------------------- 1 | export const Title = () => 'Title'; 2 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/linaria-ui-library/hocs.js: -------------------------------------------------------------------------------- 1 | export const connect = (i) => i; 2 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/linaria-ui-library/non-linaria-components.js: -------------------------------------------------------------------------------- 1 | export const Title = () => 'Title'; 2 | 3 | throw new Error('This file should not be imported'); 4 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/linaria-ui-library/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "linaria-ui-library", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | }, 6 | "linaria": { 7 | "components": "components/**/*" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/linaria-ui-library/types.ts: -------------------------------------------------------------------------------- 1 | export type ComponentType = 'class' | 'function' | 'arrow'; 2 | 3 | export Unexpected token; 4 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/loop/a.js: -------------------------------------------------------------------------------- 1 | const b = require('./b'); 2 | 3 | exports.A = 'A'; 4 | 5 | exports.smallB = b.B.toLowerCase(); 6 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/loop/ab.js: -------------------------------------------------------------------------------- 1 | const a = require('./a'); 2 | 3 | exports.AB = a.A + a.smallB; 4 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/loop/b.js: -------------------------------------------------------------------------------- 1 | const a = require('./a'); 2 | 3 | exports.B = 'B'; 4 | 5 | exports.smallA = a.A.toLowerCase(); 6 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/loop/ba.js: -------------------------------------------------------------------------------- 1 | const b = require('./b'); 2 | 3 | exports.BA = b.B + b.smallA; 4 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/loop/index.js: -------------------------------------------------------------------------------- 1 | const ab = require('./ab'); 2 | const ba = require('./ba'); 3 | 4 | exports.AB = ab.AB; 5 | exports.BA = ba.BA; 6 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/module-reexport.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./re-exports/constants'); 2 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/non-linaria-ui-library/index.js: -------------------------------------------------------------------------------- 1 | export const Title = () => 'Title'; 2 | 3 | throw new Error('This file should not be imported'); 4 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/non-linaria-ui-library/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "non-linaria-ui-library", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/objectExport.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | margin: 5, 3 | }; 4 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/re-exports/constants.js: -------------------------------------------------------------------------------- 1 | export const foo = 'foo'; 2 | export const bar = 'bar'; 3 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/re-exports/empty.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/re-exports/foo.js: -------------------------------------------------------------------------------- 1 | import * as fooStyles from './constants'; 2 | export * from '../bar'; 3 | 4 | export { fooStyles }; 5 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/re-exports/index.js: -------------------------------------------------------------------------------- 1 | export * from './empty'; 2 | export * from './foo'; 3 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/reexports.js: -------------------------------------------------------------------------------- 1 | export * from './foo'; 2 | export * from './bar'; 3 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/runNearFramePaint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The whole this file should be shaken out because it uses DOM API 3 | */ 4 | 5 | let isHidden = document.visibilityState !== 'visible'; 6 | 7 | document.addEventListener('visibilitychange', () => { 8 | isHidden = document.visibilityState !== 'visible'; 9 | }); 10 | 11 | export function runNearFramePaint(callback) { 12 | if (isHidden) { 13 | return; 14 | } else { 15 | callback(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/sample-asset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/linaria/c3721288082a9c568e99d31a3f0bd741e24524ac/packages/testkit/src/__fixtures__/sample-asset.png -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/sample-data.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Luke Skywalker", 3 | "height": "172", 4 | "mass": "77", 5 | "hair_color": "blond", 6 | "skin_color": "fair", 7 | "eye_color": "blue", 8 | "birth_year": "19BBY", 9 | "gender": "male" 10 | } 11 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/sample-script.cjs: -------------------------------------------------------------------------------- 1 | module.exports = 42; 2 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/sample-script.js: -------------------------------------------------------------------------------- 1 | module.exports = 42; 2 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/sample-typescript.tsx: -------------------------------------------------------------------------------- 1 | export default 27; 2 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/self-import.js: -------------------------------------------------------------------------------- 1 | import { constant as importedConstant } from './self-import'; 2 | 3 | export const constant = 42; 4 | export const stringConstant = importedConstant.toString(16); 5 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/sequenceExport.js: -------------------------------------------------------------------------------- 1 | let n = 0; 2 | export default ((n = 5), n); 3 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/slugify.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-plusplus, no-bitwise, default-case, no-param-reassign, prefer-destructuring */ 2 | /** 3 | * This file contains a utility to generate hashes to be used as generated class names 4 | */ 5 | 6 | /** 7 | * murmurhash2 via https://gist.github.com/raycmorgan/588423 8 | */ 9 | 10 | function UInt32(str, pos) { 11 | return ( 12 | str.charCodeAt(pos++) + 13 | (str.charCodeAt(pos++) << 8) + 14 | (str.charCodeAt(pos++) << 16) + 15 | (str.charCodeAt(pos) << 24) 16 | ); 17 | } 18 | 19 | function UInt16(str, pos) { 20 | return str.charCodeAt(pos++) + (str.charCodeAt(pos++) << 8); 21 | } 22 | 23 | function Umul32(n, m) { 24 | n |= 0; 25 | m |= 0; 26 | const nlo = n & 0xffff; 27 | const nhi = n >>> 16; 28 | return (nlo * m + (((nhi * m) & 0xffff) << 16)) | 0; 29 | } 30 | 31 | function doHash(str, seed = 0) { 32 | const m = 0x5bd1e995; 33 | const r = 24; 34 | let h = seed ^ str.length; 35 | let length = str.length; 36 | let currentIndex = 0; 37 | 38 | while (length >= 4) { 39 | let k = UInt32(str, currentIndex); 40 | 41 | k = Umul32(k, m); 42 | k ^= k >>> r; 43 | k = Umul32(k, m); 44 | 45 | h = Umul32(h, m); 46 | h ^= k; 47 | 48 | currentIndex += 4; 49 | length -= 4; 50 | } 51 | 52 | switch (length) { 53 | case 3: 54 | h ^= UInt16(str, currentIndex); 55 | h ^= str.charCodeAt(currentIndex + 2) << 16; 56 | h = Umul32(h, m); 57 | break; 58 | 59 | case 2: 60 | h ^= UInt16(str, currentIndex); 61 | h = Umul32(h, m); 62 | break; 63 | 64 | case 1: 65 | h ^= str.charCodeAt(currentIndex); 66 | h = Umul32(h, m); 67 | break; 68 | } 69 | 70 | h ^= h >>> 13; 71 | h = Umul32(h, m); 72 | h ^= h >>> 15; 73 | 74 | return h >>> 0; 75 | } 76 | 77 | export default function slugify(code) { 78 | return doHash(code).toString(36); 79 | } 80 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/ts-compiled-re-exports/constants.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | exports.bar = exports.foo = void 0; 4 | exports.foo = 'foo'; 5 | exports.bar = 'bar'; 6 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/ts-compiled-re-exports/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __exportStar = (this && this.__exportStar) || function(m, exports) { 14 | for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); 15 | }; 16 | exports.__esModule = true; 17 | __exportStar(require("./constants"), exports); 18 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/ts-data.ts: -------------------------------------------------------------------------------- 1 | export enum TestEnum { 2 | FirstValue, 3 | SecondValue, 4 | } 5 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/with-babelrc/.babelrc.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | "plugins": [ 5 | [ 6 | "module-resolver", 7 | { 8 | "alias": { 9 | "_": "./src/__fixtures__" 10 | } 11 | } 12 | ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/testkit/src/__fixtures__/with-babelrc/index.js: -------------------------------------------------------------------------------- 1 | import { styled } from '@linaria/react'; 2 | import { fooStyles } from "_/re-exports"; 3 | 4 | const value = fooStyles.foo; 5 | 6 | export const H1 = styled.h1` 7 | color: ${value}; 8 | `; 9 | -------------------------------------------------------------------------------- /packages/testkit/src/__snapshots__/transform.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`doesn't rewrite an absolute path in url() declarations 1`] = ` 4 | ".tpyglzj{background-image:url(/assets/test.jpg);} 5 | " 6 | `; 7 | 8 | exports[`rewrites a relative path in url() declarations 1`] = ` 9 | ".tpyglzj{background-image:url(../assets/test.jpg);background-image:url("../assets/test.jpg");background-image:url('../assets/test.jpg');} 10 | " 11 | `; 12 | 13 | exports[`rewrites multiple relative paths in url() declarations 1`] = ` 14 | "@font-face{font-family:Test;src:url(../assets/font.woff2) format("woff2"),url(../assets/font.woff) format("woff");} 15 | " 16 | `; 17 | -------------------------------------------------------------------------------- /packages/testkit/src/__utils__/linaria-snapshot-serializer.ts: -------------------------------------------------------------------------------- 1 | import type { WYWTransformMetadata } from '@wyw-in-js/transform'; 2 | import { withTransformMetadata } from '@wyw-in-js/transform'; 3 | 4 | type Serializer = { 5 | serialize: (value: T) => string; 6 | test: (value: unknown) => value is T; 7 | }; 8 | 9 | export default { 10 | test: withTransformMetadata, 11 | serialize: ({ wywInJS }) => ` 12 | CSS: 13 | 14 | ${Object.keys(wywInJS.rules ?? {}) 15 | .map((selector) => 16 | wywInJS.rules[selector].atom 17 | ? wywInJS.rules[selector].cssText 18 | : `${selector} {${wywInJS.rules[selector].cssText}}` 19 | ) 20 | .join('\n')} 21 | 22 | Dependencies: ${ 23 | wywInJS.dependencies?.length ? wywInJS.dependencies.join(', ') : 'NA' 24 | } 25 | `, 26 | } as Serializer<{ wywInJS: WYWTransformMetadata & { rules?: any } }>; 27 | -------------------------------------------------------------------------------- /packages/testkit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "exclude": [ 4 | // Contains broken code that should not be type-checked 5 | "src/__fixtures__/prepare-code-test-cases/dynamic-import/input.ts", 6 | "src/__fixtures__/prepare-code-test-cases/dynamic-import-param/input.ts", 7 | "src/__fixtures__/prepare-code-test-cases/for-debug/input.ts", 8 | "src/__fixtures__/linaria-ui-library/types.ts", 9 | 10 | "node_modules" 11 | ], 12 | "compilerOptions": { 13 | "paths": {}, 14 | "rootDir": "src/", 15 | "types": ["jest", "node"] 16 | }, 17 | "references": [{ "path": "../core" }, { "path": "../react" }] 18 | } 19 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | - 'website' 4 | - 'examples/**' 5 | - '!examples/gatsby/**' # too many dependencies, possible outdated 6 | - '!examples/webpack4' # legacy since node 17 7 | -------------------------------------------------------------------------------- /react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "../lib/react/index.js", 3 | "module": "../esm/react/index.js", 4 | "types": "../lib/react/index.d.ts" 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules"], 4 | "include": ["packages/**/*.ts", "packages/**/*.js", "packages/**/*.tsx"] 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": [ 3 | "node_modules", 4 | "packages/**/__dtslint__/**", 5 | "packages/**/__tests__/**", 6 | "packages/**/__fixtures__/**", 7 | "packages/**/__utils__/**", 8 | "packages/**/dist/**", 9 | "packages/**/types/**" 10 | ], 11 | "compilerOptions": { 12 | "baseUrl": ".", 13 | "composite": true, 14 | "paths": { 15 | "@linaria/*": ["./packages/*/src"] 16 | }, 17 | "jsx": "react", 18 | "lib": ["es6", "es2017", "es2018", "es2022", "ES2019.Array", "dom", "esnext.disposable"], 19 | "module": "es6", 20 | "moduleResolution": "node", 21 | "strict": true, 22 | "target": "es2022", 23 | "allowSyntheticDefaultImports": true, 24 | "esModuleInterop": true, 25 | "forceConsistentCasingInFileNames": true, 26 | "declaration": true, 27 | "typeRoots": ["node_modules/@types", "typings"], 28 | "skipLibCheck": true, 29 | "skipDefaultLibCheck": true, 30 | "preserveWatchOutput": true, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/core/*.ts", "src/react/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "dtslint/dtslint.json", 3 | "rules": { 4 | "file-name-casing": false, 5 | "interface-over-type-literal": false, 6 | "strict-export-declare-modifiers": false 7 | }, 8 | "linterOptions": { 9 | "exclude": ["node_modules/**", "packages/*/types"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "pipeline": { 4 | "build": { 5 | "dependsOn": ["^build"], 6 | "outputs": ["dist/**", "esm/**", "lib/**", "types/**"] 7 | }, 8 | "build:declarations": { 9 | "dependsOn": ["^build:declarations"], 10 | "outputs": ["types/**"] 11 | }, 12 | "check:all": { 13 | "dependsOn": ["test:dts", "typecheck", "test"] 14 | }, 15 | "lint": {}, 16 | "test": { 17 | "dependsOn": [], 18 | "outputs": [], 19 | "cache": false 20 | }, 21 | "@linaria/atomic#test": { 22 | "dependsOn": ["@linaria/core#build", "@linaria/react#build"] 23 | }, 24 | "@linaria/react#test": { 25 | "dependsOn": ["@linaria/core#build"] 26 | }, 27 | "@linaria/testkit#test": { 28 | "dependsOn": ["^build"] 29 | }, 30 | "test:dts": { 31 | "dependsOn": ["build:declarations", "^build:declarations"], 32 | "cache": false 33 | }, 34 | "typecheck": { 35 | "dependsOn": ["build:declarations", "^build:declarations"] 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /website/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "settings": { 3 | "import/core-modules": [] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /website/assets/code-sample-v4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/linaria/c3721288082a9c568e99d31a3f0bd741e24524ac/website/assets/code-sample-v4.png -------------------------------------------------------------------------------- /website/assets/code-sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/linaria/c3721288082a9c568e99d31a3f0bd741e24524ac/website/assets/code-sample.png -------------------------------------------------------------------------------- /website/assets/linaria-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | linaria-logo 6 | 7 | 15 | -------------------------------------------------------------------------------- /website/assets/linaria-logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/linaria/c3721288082a9c568e99d31a3f0bd741e24524ac/website/assets/linaria-logo@2x.png -------------------------------------------------------------------------------- /website/assets/linaria-logomark.svg: -------------------------------------------------------------------------------- 1 | 2 | linaria-logomark-black 3 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /website/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@babel/preset-env', '@babel/preset-react'], 3 | env: { 4 | server: { 5 | presets: [ 6 | require.resolve('@wyw-in-js/babel-preset'), 7 | ['@babel/preset-env', { targets: { node: 12 } }], 8 | ], 9 | plugins: [ 10 | [ 11 | 'file-loader', 12 | { 13 | publicPath: '/dist', 14 | outputPath: '/dist', 15 | }, 16 | ], 17 | ], 18 | }, 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Linaria – zero-runtime CSS in JS library 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /website/linaria.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: process.env.NODE_ENV !== 'production', 3 | }; 4 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "linaria-website", 3 | "version": "6.3.0", 4 | "private": true, 5 | "description": "Linaria website", 6 | "exports": { 7 | ".": "./src/index.jsx", 8 | "./*": "./src/*.jsx" 9 | }, 10 | "main": "./src/index.jsx", 11 | "scripts": { 12 | "build": "pnpm build:client && pnpm build:server", 13 | "build:client": "cross-env NODE_ENV=production webpack", 14 | "build:server": "cross-env NODE_ENV=production BABEL_ENV=server babel src --out-dir build", 15 | "clean": "pnpm clean:client && pnpm clean:server", 16 | "clean:client": "del dist", 17 | "clean:server": "del build", 18 | "lint:css": "stylelint src/**/*.jsx", 19 | "prebuild": "pnpm clean", 20 | "prestart": "pnpm -w prepare", 21 | "server": "node build/server", 22 | "start": "webpack serve" 23 | }, 24 | "dependencies": { 25 | "@babel/runtime": "^7.23.5", 26 | "@linaria/atomic": "workspace:^", 27 | "@linaria/core": "workspace:^", 28 | "@linaria/react": "workspace:^", 29 | "@linaria/server": "workspace:^", 30 | "babel-plugin-file-loader": "^1.1.1", 31 | "dedent": "^1.5.1", 32 | "escape-html": "^1.0.3", 33 | "ignore-styles": "^5.0.1", 34 | "koa": "^2.7.0", 35 | "koa-compress": "^3.0.0", 36 | "koa-router": "^7.4.0", 37 | "koa-send": "^5.0.0", 38 | "react": "^16.14.0", 39 | "react-dom": "^16.14.0" 40 | }, 41 | "devDependencies": { 42 | "@babel/cli": "^7.23.4", 43 | "@babel/core": "^7.23.5", 44 | "@linaria/stylelint": "workspace:^", 45 | "@wyw-in-js/babel-preset": "^0.6.0", 46 | "@wyw-in-js/webpack-loader": "^0.6.0", 47 | "babel-loader": "^8.2.5", 48 | "babel-plugin-module-resolver": "^4.1.0", 49 | "cross-env": "^7.0.3", 50 | "css-hot-loader": "^1.4.4", 51 | "css-loader": "^6.7.1", 52 | "del-cli": "^1.1.0", 53 | "html-webpack-plugin": "^5.5.3", 54 | "mini-css-extract-plugin": "^2.6.0", 55 | "stylelint": "^14.11.0", 56 | "stylelint-config-recommended": "^3.0.0", 57 | "url": "^0.11.0", 58 | "webpack": "^5.94.0", 59 | "webpack-cli": "^4.9.2", 60 | "webpack-dev-server": "^4.9.1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /website/serve.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | port: 3242, 5 | content: path.resolve(__dirname), 6 | devMiddleware: { 7 | publicPath: '/dist/', 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /website/src/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /website/src/api/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/linaria/c3721288082a9c568e99d31a3f0bd741e24524ac/website/src/api/index.js -------------------------------------------------------------------------------- /website/src/api/react/index.js: -------------------------------------------------------------------------------- 1 | export * from '@linaria/react'; 2 | -------------------------------------------------------------------------------- /website/src/components/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { css, cx } from '@linaria/atomic'; 3 | import Header from './Header'; 4 | import Hero from './Hero'; 5 | 6 | const Page = css` 7 | background: linear-gradient(to bottom right, #b24592, #f15f79); 8 | color: #fff; 9 | min-height: 100vh; 10 | text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.08); 11 | `; 12 | 13 | export default function Index() { 14 | return ( 15 |
16 |
17 | 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /website/src/components/Container.js: -------------------------------------------------------------------------------- 1 | import { styled } from '@linaria/react'; 2 | 3 | const Container = styled.div` 4 | max-width: 1140px; 5 | padding: 16px 24px; 6 | margin: 0 auto; 7 | `; 8 | 9 | export default Container; 10 | -------------------------------------------------------------------------------- /website/src/components/Example.js: -------------------------------------------------------------------------------- 1 | import { css } from '@linaria/core'; 2 | import { css as atomicCss } from '@linaria/atomic'; 3 | 4 | export const Page = css` 5 | background: linear-gradient(to bottom right, #b24592, #f15f79); 6 | color: #fff; 7 | min-height: 100vh; 8 | text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.08); 9 | `; 10 | 11 | export const AtomicPage = atomicCss` 12 | background: linear-gradient(to bottom right, #b24592, #f15f79); 13 | color: #fff; 14 | min-height: 100vh; 15 | text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.08); 16 | `; 17 | -------------------------------------------------------------------------------- /website/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import { styled } from '@linaria/react'; 2 | import React from 'react'; 3 | import constants from '../styles/constants'; 4 | import { media } from '../styles/utils'; 5 | import Container from './Container'; 6 | 7 | import logo from '../../assets/linaria-logo.svg'; 8 | 9 | export default function Header() { 10 | return ( 11 | 12 | 13 | 14 |
  • 15 | 16 | Features 17 | 18 |
  • 19 |
  • 20 | 25 | Docs 26 | 27 |
  • 28 |
  • 29 | 35 | GitHub 36 | 37 |
  • 38 |
    39 |
    40 | ); 41 | } 42 | 43 | const NavBar = styled(Container)` 44 | display: flex; 45 | flex-direction: column; 46 | justify-content: space-between; 47 | align-items: center; 48 | 49 | ${media.medium} { 50 | flex-direction: row; 51 | } 52 | `; 53 | 54 | const LogoImage = styled.img` 55 | height: 64px; 56 | margin: 16px auto; 57 | display: block; 58 | 59 | ${media.medium} { 60 | height: 48px; 61 | margin: 0; 62 | display: inline-block; 63 | vertical-align: middle; 64 | } 65 | `; 66 | 67 | const Links = styled.ul` 68 | display: flex; 69 | padding: 0; 70 | margin: 0; 71 | list-style: none; 72 | align-items: center; 73 | `; 74 | 75 | const LinkItem = styled.a` 76 | display: block; 77 | font-size: 1.2em; 78 | font-weight: 700; 79 | padding: 24px 16px; 80 | text-decoration: none; 81 | color: inherit; 82 | transition: color 0.2s; 83 | transition: 200ms; 84 | 85 | &:hover { 86 | color: inherit; 87 | } 88 | 89 | @supports (-webkit-background-clip: text) { 90 | background-image: ${constants.gradient}; 91 | /* stylelint-disable-next-line property-no-vendor-prefix */ 92 | -webkit-background-clip: text; 93 | 94 | &:hover { 95 | color: transparent; 96 | } 97 | } 98 | `; 99 | -------------------------------------------------------------------------------- /website/src/components/Hero.jsx: -------------------------------------------------------------------------------- 1 | import { styled } from '@linaria/react'; 2 | import React from 'react'; 3 | import { media } from '../styles/utils'; 4 | import Container from './Container'; 5 | 6 | import codeSample from '../../assets/code-sample-v4.png'; 7 | import linariaLogomark from '../../assets/linaria-logomark.svg'; 8 | 9 | export default function Hero() { 10 | return ( 11 | 12 | 13 | 14 | 15 | Zero-Runtime CSS in JS 16 | 17 | Write CSS in JS and get real CSS files during build. Use dynamic 18 | prop based styles with the React bindings and have them transpiled 19 | to CSS variables automatically. Great productivity with source 20 | maps and linting support. 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ); 36 | } 37 | 38 | const HeroContainer = styled.main` 39 | position: relative; 40 | 41 | ${media.large} { 42 | padding: 64px 0; 43 | background-image: url(${linariaLogomark}); 44 | background-repeat: no-repeat; 45 | background-position: bottom right; 46 | } 47 | `; 48 | 49 | const Row = styled.div` 50 | ${media.large} { 51 | display: flex; 52 | align-items: center; 53 | justify-content: space-between; 54 | } 55 | `; 56 | 57 | const LeftColumn = styled.div` 58 | text-align: center; 59 | flex: 3; 60 | 61 | ${media.large} { 62 | text-align: left; 63 | } 64 | `; 65 | 66 | const RightColumn = styled.div` 67 | text-align: center; 68 | flex: 2; 69 | z-index: 1; 70 | `; 71 | 72 | const Heading = styled.h1` 73 | font-weight: 700; 74 | font-size: 56px; 75 | `; 76 | 77 | const Description = styled.p` 78 | margin-bottom: 60px; 79 | `; 80 | 81 | const Button = styled.button` 82 | display: inline-block; 83 | appearance: none; 84 | background: none; 85 | border: 0; 86 | padding: 16px 24px; 87 | color: inherit; 88 | font-size: inherit; 89 | font-weight: 700; 90 | font-family: inherit; 91 | text-transform: uppercase; 92 | text-decoration: none; 93 | box-shadow: inset 0 0 0 2px currentColor, 1px 1px 1px rgba(0, 0, 0, 0.08); 94 | border-radius: 30px; 95 | cursor: pointer; 96 | transition: color 200ms, background 200ms; 97 | 98 | &:hover { 99 | color: #d2356d; 100 | box-shadow: inset 0 0 0 2px transparent, 1px 1px 1px rgba(0, 0, 0, 0.08); 101 | background: linear-gradient( 102 | to right, 103 | hsl(180, 100%, 70%), 104 | hsl(64, 57%, 82%), 105 | hsl(0, 100%, 84%) 106 | ); 107 | } 108 | `; 109 | 110 | const CodeSample = styled.img` 111 | width: 100%; 112 | height: auto; 113 | margin: 64px 24px; 114 | padding: 20px; 115 | border-radius: 5px; 116 | max-width: calc(100% - 48px); 117 | box-shadow: 3px 3px 32px rgba(0, 0, 0, 0.32); 118 | background-color: #272727; 119 | 120 | ${media.large} { 121 | margin: 24px; 122 | } 123 | `; 124 | -------------------------------------------------------------------------------- /website/src/index.jsx: -------------------------------------------------------------------------------- 1 | import { css } from '@linaria/core'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import App from './components/App'; 5 | import constants from './styles/constants'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // eslint-disable-next-line import/prefer-default-export 10 | export const globals = css` 11 | :global() { 12 | html { 13 | box-sizing: border-box; 14 | height: 100%; 15 | width: 100%; 16 | } 17 | 18 | body { 19 | margin: 0; 20 | padding: 0; 21 | height: 100%; 22 | width: 100%; 23 | font-family: ${constants.fontFamily}; 24 | font-size: 20px; 25 | line-height: 1.42857; 26 | } 27 | 28 | *, 29 | *:before, 30 | *:after { 31 | box-sizing: inherit; 32 | } 33 | } 34 | `; 35 | -------------------------------------------------------------------------------- /website/src/server.jsx: -------------------------------------------------------------------------------- 1 | import 'ignore-styles'; 2 | 3 | import fs from 'fs'; 4 | import path from 'path'; 5 | import crypto from 'crypto'; 6 | import { collect } from '@linaria/server'; 7 | import Koa from 'koa'; 8 | import Router from 'koa-router'; 9 | import compress from 'koa-compress'; 10 | import send from 'koa-send'; 11 | import dedent from 'dedent'; 12 | import React from 'react'; 13 | import ReactDOMServer from 'react-dom/server'; 14 | import config from '../serve.config'; 15 | import App from './components/App'; 16 | 17 | const cache = {}; 18 | const css = fs.readFileSync(path.join(__dirname, '../dist/styles.css'), 'utf8'); 19 | const app = new Koa(); 20 | const router = new Router(); 21 | 22 | app.use(compress()); 23 | 24 | router.get('/', async (ctx) => { 25 | const html = ReactDOMServer.renderToStaticMarkup(); 26 | 27 | const { critical, other } = collect(html, css); 28 | const slug = crypto.createHash('md5').update(other).digest('hex'); 29 | 30 | cache[slug] = other; 31 | 32 | ctx.type = 'html'; 33 | ctx.body = dedent` 34 | 35 | 36 | 37 | 38 | 39 | 40 | Linaria – zero-runtime CSS in JS library 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
    ${html}
    49 | 50 | 51 | 52 | 53 | 54 | `; 55 | }); 56 | 57 | router.get('/dist/:path+', async (ctx) => { 58 | await send(ctx, path.join('dist', ctx.params.path)); 59 | }); 60 | 61 | router.get('/styles/:slug', async (ctx) => { 62 | ctx.type = 'text/css'; 63 | ctx.body = cache[ctx.params.slug]; 64 | }); 65 | 66 | app.use(router.routes()); 67 | 68 | app.listen(config.port); 69 | 70 | // eslint-disable-next-line no-console 71 | console.log(`Listening on http://localhost:${config.port}`); 72 | -------------------------------------------------------------------------------- /website/src/styles/constants.js: -------------------------------------------------------------------------------- 1 | export default { 2 | fontFamily: 3 | '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", Helvetica, sans-serif', 4 | gradient: 5 | 'linear-gradient(to right, hsl(180, 100%, 70%), hsl(64, 57%, 82%), hsl(0, 100%, 90%))', 6 | }; 7 | -------------------------------------------------------------------------------- /website/src/styles/utils.js: -------------------------------------------------------------------------------- 1 | export const breakpoints = { 2 | medium: 640, 3 | large: 1024, 4 | }; 5 | 6 | export const media = Object.keys(breakpoints).reduce((acc, item) => { 7 | acc[item] = `@media screen and (min-width: ${breakpoints[item]}px)`; 8 | return acc; 9 | }, {}); 10 | -------------------------------------------------------------------------------- /website/stylelint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'stylelint-config-recommended', 4 | require.resolve('@linaria/stylelint'), 5 | ], 6 | }; 7 | -------------------------------------------------------------------------------- /website/webpack.config.js: -------------------------------------------------------------------------------- 1 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 2 | const { join } = require('path'); // eslint-disable-line import/no-extraneous-dependencies 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const { WYWinJSDebugPlugin } = require('@wyw-in-js/webpack-loader'); 5 | 6 | const dev = process.env.NODE_ENV !== 'production'; 7 | 8 | module.exports = { 9 | mode: dev ? 'development' : 'production', 10 | devtool: 'source-map', 11 | entry: { 12 | app: './src/index', 13 | }, 14 | output: { 15 | path: join(__dirname, 'dist'), 16 | filename: '[name].bundle.js', 17 | }, 18 | optimization: { 19 | emitOnErrors: false, 20 | }, 21 | plugins: [ 22 | new WYWinJSDebugPlugin({ 23 | dir: 'wyw-debug', 24 | print: true, 25 | }), 26 | new MiniCssExtractPlugin({ filename: 'styles.css' }), 27 | new HtmlWebpackPlugin({ 28 | title: 'Linaria – zero-runtime CSS in JS library', 29 | templateContent: ` 30 | 31 | 32 | 33 | 34 | 35 | 36 |
    37 | 38 | 39 | `, 40 | }), 41 | ], 42 | resolve: { 43 | extensions: ['.js', '.jsx'], 44 | }, 45 | module: { 46 | rules: [ 47 | { 48 | test: /\.jsx?$/, 49 | exclude: /node_modules/, 50 | use: [ 51 | { loader: 'babel-loader' }, 52 | { 53 | loader: require.resolve('@wyw-in-js/webpack-loader'), 54 | options: { sourceMap: dev }, 55 | }, 56 | ], 57 | }, 58 | { 59 | test: /\.css$/, 60 | use: [ 61 | 'css-hot-loader', 62 | MiniCssExtractPlugin.loader, 63 | { 64 | loader: 'css-loader', 65 | options: { sourceMap: dev }, 66 | }, 67 | ], 68 | }, 69 | { 70 | test: /\.(png|jpg|gif|svg)$/, 71 | type: 'asset/resource', 72 | }, 73 | ], 74 | }, 75 | devServer: { 76 | static: { 77 | directory: join(__dirname, 'dist'), 78 | }, 79 | hot: true, 80 | historyApiFallback: { 81 | index: 'index.html', 82 | }, 83 | }, 84 | }; 85 | --------------------------------------------------------------------------------