├── .eslintignore
├── commitlint.config.js
├── example
├── index.html
├── constants.js
└── App.js
├── src
├── __fixtures__
│ ├── css.js
│ ├── index.js
│ └── styled.js
├── index.js
├── __tests__
│ └── index.test.js
└── LinariaAsset.js
├── .codecov.yml
├── .babelrc
├── .eslintrc
├── .release-it.json
├── .editorconfig
├── .circleci
├── comment-artifacts.sh
└── config.yml
├── LICENSE
├── .gitignore
├── package.json
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | __fixtures__/
2 | __output__/
3 |
4 | dist/
5 | node_modules/
6 |
7 | .cache/
8 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional'],
3 | };
4 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/__fixtures__/css.js:
--------------------------------------------------------------------------------
1 | import { css } from 'linaria';
2 |
3 | const answer = 42;
4 |
5 | export const title = css`
6 | font-size: 16px;
7 | `;
8 |
--------------------------------------------------------------------------------
/src/__fixtures__/index.js:
--------------------------------------------------------------------------------
1 | import { title } from './css';
2 | import { Title } from './styled';
3 |
4 | document.getElementById('title').className = title;
5 |
--------------------------------------------------------------------------------
/src/__fixtures__/styled.js:
--------------------------------------------------------------------------------
1 | import { styled } from 'linaria/react';
2 |
3 | const answer = 42;
4 |
5 | export const Title = styled.div`
6 | font-size: 16px;
7 | `;
8 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "targets": {
7 | "node": "current"
8 | }
9 | }
10 | ],
11 | "@babel/preset-react"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/example/constants.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | fontFamily:
3 | 'Noto Sans, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
4 | primaryColor: '#f15f79',
5 | };
6 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | 'extends': '@callstack/eslint-config',
3 | 'rules':
4 | {
5 | 'global-require': 'off',
6 | 'import/no-extraneous-dependencies':
7 | ['error', { 'devDependencies': true }],
8 | 'react-native/no-raw-text': 'off',
9 | },
10 | }
11 |
--------------------------------------------------------------------------------
/.release-it.json:
--------------------------------------------------------------------------------
1 | {
2 | "git": {
3 | "commitMessage": "chore: release %s",
4 | "tagName": "v%s"
5 | },
6 | "npm": {
7 | "publish": true
8 | },
9 | "github": {
10 | "release": true
11 | },
12 | "plugins": {
13 | "@release-it/conventional-changelog": {
14 | "preset": "angular"
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable func-names */
2 |
3 | module.exports = function(bundler) {
4 | const LinariaAsset = require.resolve('./LinariaAsset');
5 |
6 | bundler.addAssetType('js', LinariaAsset);
7 | bundler.addAssetType('jsx', LinariaAsset);
8 |
9 | bundler.addAssetType('ts', LinariaAsset);
10 | bundler.addAssetType('tsx', LinariaAsset);
11 | };
12 |
--------------------------------------------------------------------------------
/example/App.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { styled } from 'linaria/react';
4 |
5 | import constants from './constants';
6 |
7 | const Title = styled.h1`
8 | font-family: ${constants.fontFamily};
9 | font-size: 18px;
10 | color: ${() => constants.primaryColor};
11 | `;
12 |
13 | function App() {
14 | return Hello world;
15 | }
16 |
17 | ReactDOM.render(, document.getElementById('root'));
18 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.circleci/comment-artifacts.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | URL="https://callstack-github-bot.herokuapp.com/comment"
4 | LINK="https://$CIRCLE_BUILD_NUM-172142945-gh.circle-artifacts.com/$CIRCLE_NODE_INDEX/coverage/lcov-report/index.html"
5 | TEMPLATE="Hey @{{user.login}}, thank you for your pull request 🤗.\nThe coverage report for this branch can be viewed [here]($LINK)."
6 |
7 | read -r -d '' DATA << EOM
8 | {
9 | "pull_request": "$CIRCLE_PULL_REQUEST",
10 | "template": "$TEMPLATE",
11 | "test": {
12 | "type": "string",
13 | "data": "The coverage report for this branch can be viewed"
14 | },
15 | "update": true
16 | }
17 | EOM
18 |
19 | curl \
20 | -H "Content-Type: application/json" \
21 | -d "$DATA" \
22 | -X POST $URL
23 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### OS Junk ###
2 | .DS_Store
3 | *~
4 |
5 | ### Node ###
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (http://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # Typescript v1 declaration files
45 | typings/
46 |
47 | # Optional npm cache directory
48 | .npm
49 |
50 | # Optional eslint cache
51 | .eslintcache
52 |
53 | # Optional REPL history
54 | .node_repl_history
55 |
56 | # Output of 'npm pack'
57 | *.tgz
58 |
59 | # Yarn Integrity file
60 | .yarn-integrity
61 |
62 | # dotenv environment variables file
63 | .env
64 |
65 | # generated files
66 | lib/
67 | dist/
68 | build/
69 |
70 | __output__
71 |
72 | .linaria-cache
73 | .cache
74 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "parcel-plugin-linaria",
3 | "version": "0.1.2",
4 | "main": "src/index.js",
5 | "license": "MIT",
6 | "keywords": [
7 | "parcel",
8 | "linaria"
9 | ],
10 | "scripts": {
11 | "lint": "eslint .",
12 | "test": "jest",
13 | "example": "parcel example/index.html",
14 | "release": "release-it"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.5.0",
18 | "@babel/preset-env": "^7.5.0",
19 | "@babel/preset-react": "^7.0.0",
20 | "@callstack/eslint-config": "^6.1.0",
21 | "@commitlint/config-conventional": "^8.0.0",
22 | "@release-it/conventional-changelog": "^1.1.0",
23 | "codecov": "^3.5.0",
24 | "commitlint": "^8.0.0",
25 | "eslint": "^6.0.1",
26 | "husky": "^3.0.0",
27 | "jest": "^24.8.0",
28 | "linaria": "^1.1.4",
29 | "parcel": "^1.12.3",
30 | "parcel-assert-bundle-tree": "^1.0.0",
31 | "parcel-plugin-linaria": "link:.",
32 | "prettier": "^1.18.2",
33 | "react": "^16.8.6",
34 | "react-dom": "^16.8.6",
35 | "release-it": "^12.3.2"
36 | },
37 | "peerDependencies": {
38 | "linaria": ">= 1.0.0",
39 | "parcel": ">= 1.12.0"
40 | },
41 | "husky": {
42 | "hooks": {
43 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
44 | "pre-commit": "yarn lint && yarn test"
45 | }
46 | },
47 | "jest": {
48 | "testEnvironment": "node",
49 | "testRegex": "/__tests__/.*\\.(test|spec)\\.js$",
50 | "watchPathIgnorePatterns": [
51 | "__output__"
52 | ]
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/__tests__/index.test.js:
--------------------------------------------------------------------------------
1 | const Bundler = require('parcel');
2 | const assertBundleTree = require('parcel-assert-bundle-tree');
3 | const path = require('path');
4 | const LinariaPlugin = require('../index');
5 |
6 | const bundleFile = async filename => {
7 | const bundler = new Bundler(filename, {
8 | outDir: path.join(__dirname, '..', '__output__'),
9 | sourceMaps: true,
10 | watch: false,
11 | cache: false,
12 | hmr: false,
13 | logLevel: 0,
14 | });
15 |
16 | await LinariaPlugin(bundler);
17 |
18 | const bundle = await bundler.bundle();
19 |
20 | return bundle;
21 | };
22 |
23 | it('bundles file with css tag', async () => {
24 | const bundle = await bundleFile(
25 | path.join(__dirname, '..', '__fixtures__', 'css.js')
26 | );
27 |
28 | assertBundleTree(bundle, {
29 | name: 'css.js',
30 | assets: ['css.js', 'css.js', 'cx.js', 'index.js'],
31 | childBundles: [
32 | {
33 | type: 'css',
34 | assets: ['css.js'],
35 | },
36 | { type: 'map' },
37 | ],
38 | });
39 | });
40 |
41 | it('bundles file with styled tag', async () => {
42 | const bundle = await bundleFile(
43 | path.join(__dirname, '..', '__fixtures__', 'styled.js')
44 | );
45 |
46 | assertBundleTree(bundle, {
47 | name: 'styled.js',
48 | childBundles: [
49 | { type: 'map' },
50 | {
51 | type: 'css',
52 | assets: ['styled.js'],
53 | },
54 | ],
55 | });
56 | });
57 |
58 | it('bundles combined file', async () => {
59 | const bundle = await bundleFile(
60 | path.join(__dirname, '..', '__fixtures__', 'index.js')
61 | );
62 |
63 | assertBundleTree(bundle, {
64 | name: 'index.js',
65 | childBundles: [
66 | {
67 | type: 'css',
68 | assets: ['css.js', 'styled.js'],
69 | },
70 | { type: 'map' },
71 | ],
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | defaults: &defaults
3 | docker:
4 | - image: circleci/node:10
5 | working_directory: ~/project
6 | jobs:
7 | install-dependencies:
8 | <<: *defaults
9 | steps:
10 | - checkout
11 | - attach_workspace:
12 | at: ~/project
13 | - restore_cache:
14 | keys:
15 | - dependencies-{{ checksum "package.json" }}
16 | - dependencies-
17 | - run: |
18 | yarn install --frozen-lockfile
19 | - save_cache:
20 | key: dependencies-{{ checksum "package.json" }}
21 | paths: node_modules
22 | - persist_to_workspace:
23 | root: .
24 | paths: .
25 |
26 | lint:
27 | <<: *defaults
28 | steps:
29 | - attach_workspace:
30 | at: ~/project
31 | - run: |
32 | yarn lint
33 |
34 | unit-tests:
35 | <<: *defaults
36 | steps:
37 | - attach_workspace:
38 | at: ~/project
39 | - run: |
40 | yarn test --coverage
41 | cat ./coverage/lcov.info | ./node_modules/.bin/codecov
42 | bash .circleci/comment-artifacts.sh
43 | - store_artifacts:
44 | path: coverage
45 | destination: coverage
46 |
47 | build-example:
48 | <<: *defaults
49 | steps:
50 | - attach_workspace:
51 | at: ~/project
52 | - run: |
53 | yarn parcel build example/index.html
54 | - store_artifacts:
55 | path: dist
56 | destination: dist
57 |
58 | workflows:
59 | version: 2
60 | build-and-test:
61 | jobs:
62 | - install-dependencies
63 | - lint:
64 | requires:
65 | - install-dependencies
66 | - unit-tests:
67 | requires:
68 | - install-dependencies
69 | - build-example:
70 | requires:
71 | - install-dependencies
72 |
--------------------------------------------------------------------------------
/src/LinariaAsset.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const Module = require('module');
3 | const JSAsset = require('parcel/src/assets/JSAsset');
4 | const transform = require('linaria/lib/transform');
5 |
6 | const RESULT = Symbol('linaria-transform-result');
7 |
8 | class LinariaAsset extends JSAsset {
9 | async pretransform() {
10 | if (!/node_modules/.test(this.name)) {
11 | const result = transform(this.contents, {
12 | filename: this.name,
13 | });
14 |
15 | this[RESULT] = result;
16 | this.contents = result.code;
17 | this.ast = null;
18 | } else {
19 | this[RESULT] = {};
20 | }
21 |
22 | await super.pretransform();
23 | }
24 |
25 | collectDependencies() {
26 | const { dependencies } = this[RESULT];
27 |
28 | if (dependencies) {
29 | dependencies.forEach(dep => {
30 | try {
31 | const resolved = Module._resolveFilename(dep, {
32 | id: this.name,
33 | filename: this.name,
34 | paths: Module._nodeModulePaths(path.dirname(this.name)),
35 | });
36 |
37 | this.addDependency(resolved, { includedInParent: true });
38 | } catch (e) {
39 | // eslint-disable-next-line no-console
40 | console.warn(`Failed to add dependency '${dep}' for ${this.name}`, e);
41 | }
42 | });
43 | }
44 |
45 | super.collectDependencies();
46 | }
47 |
48 | async generate() {
49 | const { cssText } = this[RESULT];
50 | const output = [...((await super.generate()) || [])];
51 |
52 | if (cssText) {
53 | output.push({
54 | type: 'css',
55 | value: cssText,
56 | final: true,
57 | });
58 |
59 | if (this.options.hmr) {
60 | this.addDependency('_css_loader');
61 |
62 | const js = output.find(o => o.type === 'js');
63 |
64 | if (js) {
65 | js.value += `
66 | ;(function() {
67 | var reloadCSS = require('_css_loader');
68 | module.hot.dispose(reloadCSS);
69 | module.hot.accept(reloadCSS);
70 | })();
71 | `;
72 | }
73 | }
74 | }
75 |
76 | return output;
77 | }
78 | }
79 |
80 | module.exports = LinariaAsset;
81 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # parcel-plugin-linaria
2 |
3 | [![Build Status][build-badge]][build]
4 | [![Code Coverage][coverage-badge]][coverage]
5 | [![Version][version-badge]][package]
6 | [![MIT License][license-badge]][license]
7 |
8 | [![PRs Welcome][prs-welcome-badge]][prs-welcome]
9 | [![Chat][chat-badge]][chat]
10 | [![Sponsored by Callstack][callstack-badge]][callstack]
11 |
12 | A plugin to use [Linaria](https://github.com/callstack/linaria) with [Parcel](https://parceljs.org/).
13 |
14 | ## Installation
15 |
16 | ```sh
17 | yarn add --dev parcel-plugin-linaria
18 | ```
19 |
20 | or
21 |
22 | ```sh
23 | npm install --save-dev parcel-plugin-linaria
24 | ```
25 |
26 | ## Usage
27 |
28 | The plugin works without any extra configuration. See [Linaria's docs](https://github.com/callstack/linaria/blob/master/docs/CONFIGURATION.md) if you want to configure the library.
29 |
30 | ## Limitations
31 |
32 | - CSS source maps are not supported because [Parcel doesn't support them](https://github.com/parcel-bundler/parcel/issues/1824).
33 | - The plugin works by extending Parcel's `JSAsset` due to how Parcel works. Any other plugins which do the same might have conflicts.
34 |
35 | ## License
36 |
37 | MIT License
38 |
39 | ## Made with ❤️ at Callstack
40 |
41 | `parcel-plugin-linaria` is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. [Callstack](https://callstack.com) is a group of React and React Native geeks, contact us at [hello@callstack.com](mailto:hello@callstack.com) if you need any help with these or just want to say hi!
42 |
43 |
44 | [build-badge]: https://img.shields.io/circleci/project/github/callstack/parcel-plugin-linaria/master.svg?style=flat-square
45 | [build]: https://circleci.com/gh/callstack/parcel-plugin-linaria
46 | [coverage-badge]: https://img.shields.io/codecov/c/github/callstack/parcel-plugin-linaria.svg?style=flat-square
47 | [coverage]: https://codecov.io/github/callstack/parcel-plugin-linaria
48 | [version-badge]: https://img.shields.io/npm/v/parcel-plugin-linaria.svg?style=flat-square
49 | [package]: https://www.npmjs.com/package/parcel-plugin-linaria
50 | [license-badge]: https://img.shields.io/npm/l/parcel-plugin-linaria.svg?style=flat-square
51 | [license]: https://opensource.org/licenses/MIT
52 | [prs-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
53 | [prs-welcome]: http://makeapullrequest.com
54 | [chat-badge]: https://img.shields.io/discord/426714625279524876.svg?style=flat-square&colorB=758ED3
55 | [chat]: https://discord.gg/zwR2Cdh
56 | [callstack-badge]: https://callstack.com/images/callstack-badge.svg
57 | [callstack]: https://callstack.com/open-source/?utm_source=github.com&utm_medium=referral&utm_campaign=linaria&utm_term=readme
58 |
--------------------------------------------------------------------------------