├── .eslintrc.js
├── .gitignore
├── .npmrc
├── .nvmrc
├── .size.json
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── __tests__
└── index.tsx
├── css
└── package.json
├── example
├── app.tsx
├── assets
│ └── .gitkeep
├── index.html
├── index.tsx
├── styled.ts
└── utils.ts
├── jest.config.js
├── package.json
├── react
└── package.json
├── src
├── css
│ ├── correct-order.ts
│ ├── index.ts
│ └── processImportedStyles.ts
├── imported.spec.ts
├── imported.ts
├── index.ts
├── plugin.ts
├── react
│ ├── Atoms.tsx
│ ├── Chunk.tsx
│ ├── Load.tsx
│ ├── context.tsx
│ └── index.ts
├── tracker.ts
├── types.ts
└── utils
│ ├── intent-the-order.spec.ts
│ └── intent-the-order.ts
├── tsconfig.json
└── yarn.lock
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['plugin:@typescript-eslint/recommended', 'plugin:import/typescript', 'plugin:react-hooks/recommended'],
3 | parser: '@typescript-eslint/parser',
4 | plugins: ['@typescript-eslint', 'prettier', 'import'],
5 | rules: {
6 | '@typescript-eslint/ban-ts-ignore': 0,
7 | '@typescript-eslint/no-var-requires': 0,
8 | '@typescript-eslint/camelcase': 0,
9 | 'import/order': [
10 | 'error',
11 | {
12 | 'newlines-between': 'always-and-inside-groups',
13 | alphabetize: {
14 | order: 'asc',
15 | },
16 | groups: ['builtin', 'external', 'internal', ['parent', 'index', 'sibling']],
17 | },
18 | ],
19 | },
20 | settings: {
21 | 'import/parsers': {
22 | '@typescript-eslint/parser': ['.ts', '.tsx'],
23 | },
24 | 'import/resolver': {
25 | typescript: {
26 | alwaysTryTypes: true,
27 | },
28 | },
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | /lib/
3 | /dist/
4 | coverage
5 | .DS_Store
6 | .nyc_output
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.yarnpkg.com
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18
--------------------------------------------------------------------------------
/.size.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "dist/es2015/react/index.js",
4 | "passed": true,
5 | "size": 2710,
6 | "sizeLimit": 3000
7 | }
8 | ]
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '8'
4 | cache: yarn
5 | script:
6 | - yarn
7 | - yarn test:ci
8 | notifications:
9 | email: false
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.3.1](https://github.com/theKashey/webpack-imported/compare/v1.3.0...v1.3.1) (2024-08-08)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * added React 18 in peerDeps ([81b42a4](https://github.com/theKashey/webpack-imported/commit/81b42a4b8f4e879fa33cc9cf90140cd7e6f41224))
7 |
8 |
9 |
10 | # [1.3.0](https://github.com/theKashey/webpack-imported/compare/v1.2.2...v1.3.0) (2024-03-14)
11 |
12 |
13 | ### Bug Fixes
14 |
15 | * fixes alias resolver to allow arrays ([07b7f35](https://github.com/theKashey/webpack-imported/commit/07b7f3590ec1e04a7a62fdef1f6730d7e8b32af0))
16 | * places the alias inside the reduce ([83bf10c](https://github.com/theKashey/webpack-imported/commit/83bf10ca6ad6d1d11b2a1ab46f5668b3eddf4ecd))
17 | * ugraded webpack types to v5 ([4ccd8b4](https://github.com/theKashey/webpack-imported/commit/4ccd8b456bfaee94aaaef84eada9a0b59c810434))
18 |
19 |
20 |
21 | ## [1.2.2](https://github.com/theKashey/webpack-imported/compare/v1.2.1...v1.2.2) (2021-04-13)
22 |
23 |
24 |
25 | ## [1.2.1](https://github.com/theKashey/webpack-imported/compare/v1.2.0...v1.2.1) (2021-04-13)
26 |
27 |
28 |
29 | # [1.2.0](https://github.com/theKashey/webpack-imported/compare/v1.1.2...v1.2.0) (2021-04-13)
30 |
31 |
32 |
33 | ## [1.1.2](https://github.com/theKashey/webpack-imported/compare/v1.1.1...v1.1.2) (2020-12-23)
34 |
35 |
36 |
37 | ## [1.1.1](https://github.com/theKashey/webpack-imported/compare/v1.1.0...v1.1.1) (2020-12-10)
38 |
39 |
40 | ### Bug Fixes
41 |
42 | * use key on chunks ([6735f4f](https://github.com/theKashey/webpack-imported/commit/6735f4f0144fa09adc2a55d193b84cf441c5f3e5))
43 |
44 |
45 |
46 | # [1.1.0](https://github.com/theKashey/webpack-imported/compare/v1.0.0...v1.1.0) (2020-11-23)
47 |
48 |
49 | ### Features
50 |
51 | * allow direct fs writes ([ceae2f1](https://github.com/theKashey/webpack-imported/commit/ceae2f1db457da05feb67004bdf591cb867c9f54))
52 |
53 |
54 |
55 | # [1.0.0](https://github.com/theKashey/webpack-imported/compare/v0.0.7...v1.0.0) (2020-07-30)
56 |
57 |
58 |
59 | ## [0.0.7](https://github.com/theKashey/webpack-imported/compare/v0.0.6...v0.0.7) (2020-07-19)
60 |
61 |
62 |
63 | ## [0.0.6](https://github.com/theKashey/webpack-imported/compare/v0.0.5...v0.0.6) (2020-07-19)
64 |
65 |
66 | ### Bug Fixes
67 |
68 | * prefetch is not preload ([4296349](https://github.com/theKashey/webpack-imported/commit/4296349025f31e49653f3f4feadb7a78a082f6d0))
69 |
70 |
71 |
72 | ## [0.0.5](https://github.com/theKashey/webpack-imported/compare/v0.0.4...v0.0.5) (2020-07-19)
73 |
74 |
75 |
76 | ## [0.0.4](https://github.com/theKashey/webpack-imported/compare/v0.0.3...v0.0.4) (2020-07-19)
77 |
78 |
79 | ### Bug Fixes
80 |
81 | * improve documentation ([6e1a8c9](https://github.com/theKashey/webpack-imported/commit/6e1a8c99b558e4534522bb13bd9877f1b62960e8))
82 |
83 |
84 |
85 | ## [0.0.3](https://github.com/theKashey/webpack-imported/compare/65ab5f6ded3015175b9bfd18d6cbbfa4e690be9b...v0.0.3) (2019-10-28)
86 |
87 |
88 | ### Features
89 |
90 | * more options for Script Tag ([65ab5f6](https://github.com/theKashey/webpack-imported/commit/65ab5f6ded3015175b9bfd18d6cbbfa4e690be9b))
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Anton Korzunov
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | webpack-imported
2 | ======
3 | We'll get your asses imported in a right way.
4 |
5 | > 📝 stats-webpack-plugin and 💩webpack-flush-chunks had a baby!
6 |
7 | > code splitting, prefetching, and resource management.
8 |
9 | WebpackPlugin + ServerSide API + React Components(separate entrypoint)
10 |
11 | # Server side API
12 | ## Webpack plugin
13 | ```js
14 | const {ImportedPlugin} = require('webpack-imported');
15 |
16 | module.exports = {
17 | plugins: [
18 | new ImportedPlugin('imported.json')
19 | ]
20 | };
21 | ```
22 | This will output `imported.json` as a one of the emitted assets, with all the information carefully sorted.
23 |
24 | ## Stat.json
25 | If you have only `stat.json` generated somehow you can convert into into "imported" format
26 | ```js
27 | import {importStats} from "webpack-imported";
28 | import stats from 'build/stats.json';
29 |
30 | const importedStat = importStats(stats);
31 | ```
32 |
33 | ## SSR API
34 | - `importedAssets(stats, chunks, [tracker])` - return all assets associated with provided chunks.
35 | Could be provided a `tracker` to prevent duplications between runs.
36 | - `createImportedTracker()` - creates a duplication prevention tracker
37 |
38 | ```js
39 | import {importedAssets} from "webpack-imported";
40 | import importedStat from "build/imported.json"; // this file has to be generated
41 |
42 | const relatedAssets = importedAssets(importedStat, ['main']); // main is your "main" bundle
43 |
44 | relatedAssets.scripts.load // list scripts to load
45 | relatedAssets.scripts.preload // list scripts to preload
46 | relatedAssets.styles.load // list styles to load
47 | relatedAssets.styles.preload // list styles to preload
48 |
49 | importedStat.config.publicPath // public path used at build time
50 | ```
51 |
52 | with tracking
53 | ```js
54 | import {importedAssets, createImportedTracker} from "webpack-imported";
55 | import importedStat from "build/imported.json"; // this file has to be generated
56 |
57 | const tracker = createImportedTracker();
58 | const relatedAssets1 = importedAssets(importedStat, ['main'], tracker);
59 | // use scripts and styles
60 |
61 | const relatedAssets2 = importedAssets(importedStat, ['home'], tracker);
62 | // render only new scripts and styles
63 | ```
64 |
65 | # Client side API
66 |
67 | ## React bindings (for SSR)
68 | - `createImportedTracker()` - creates a duplication prevention tracker
69 | - `WebpackImportedProvider` - wires tracker down to React context
70 | - `WebpackImport` - chunk importer
71 | - `processImportedStyles` - helper for critical styles.
72 | ```js
73 | import {createImportedTracker, WebpackImportedProvider, WebpackImport} from "webpack-imported/react";
74 | import importedStat from "build/imported.json";
75 |
76 | const tracker = createImportedTracker();// this is optional, only needed if your render is multipart(head/body)
77 |
78 |
79 |
80 |
81 | ```
82 |
83 | `WebpackImport` has many props:
84 | - [`preload`=false] - only preloads resources. If preload is set resources would be loaded via network, but not executed.
85 | Never use this option for the main chunk.
86 | - [`anonymous`=false] - should it be loaded as anonymous
87 | - [`async`=true] - loads scripts with `async` attribute, uses `deferred` in other case.
88 | - [`module`=false] - loads scripts with `module` attribute
89 | - [`critical-styles`=false] - enabled critical styles handling. No styles would be loaded or prefetched,
90 | but system will leave extra markup to prevent `MiniCssExtractPlugin` from adding them by itself.
91 | With this option enabled __you have to call__ `processImportedStyles` after the application starts to load the missing styles.
92 |
93 |
94 | # Related
95 | ### Get stats from webpack
96 | - [stats-webpack-plugin](https://github.com/unindented/stats-webpack-plugin)
97 | - [loadable components webpack plugin](https://github.com/smooth-code/loadable-components/tree/master/packages/webpack-plugin)
98 |
99 | ### Handle chunks dependencies
100 | - [webpack-flush-chunks](https://github.com/faceyspacey/webpack-flush-chunks)
101 |
102 | ### React Lazy Loading
103 | - [react-imported-component](https://github.com/theKashey/react-imported-component)
104 |
105 | ### CSS Critical extraction
106 | - [used-styles](https://github.com/theKashey/used-styles)
107 |
108 | # Licence
109 | MIT
110 |
--------------------------------------------------------------------------------
/__tests__/index.tsx:
--------------------------------------------------------------------------------
1 | import '../src/index';
2 |
3 | describe('Does nothing', () => {
4 | it('Just works', () => {
5 | expect(1).toEqual(1);
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/css/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "main": "../dist/es5/css/index.js",
4 | "jsnext:main": "../dist/es2015/css/index.js",
5 | "module": "../dist/es2015/css/index.js",
6 | "types": "../dist/es2015/css/index.d.ts"
7 | }
8 |
--------------------------------------------------------------------------------
/example/app.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {Component} from 'react';
3 | import {GHCorner} from 'react-gh-corner';
4 | import {AppWrapper} from './styled';
5 | export interface AppState {
6 |
7 | }
8 | const repoUrl = 'https://github.com/zzarcon/';
9 | export default class App extends Component <{}, AppState> {
10 | state: AppState = {
11 |
12 | }
13 |
14 | render() {
15 | return (
16 |
17 |
18 | Example!
19 |
20 | )
21 | }
22 | }
--------------------------------------------------------------------------------
/example/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theKashey/webpack-imported/2f288d0650a0d11797416241b40a40c9f3cbdbf8/example/assets/.gitkeep
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Example
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as ReactDOM from 'react-dom';
3 | import App from './app';
4 |
5 | ReactDOM.render(, document.getElementById('app'));
6 |
--------------------------------------------------------------------------------
/example/styled.ts:
--------------------------------------------------------------------------------
1 | import styled, {injectGlobal} from 'styled-components';
2 |
3 | injectGlobal`
4 | body {
5 | font-family: Helvetica;
6 | background-color: #D8D1F5;
7 | }
8 |
9 | * {
10 | box-sizing: content-box;
11 | }
12 | `;
13 |
14 | export const AppWrapper = styled.div`
15 |
16 | `;
--------------------------------------------------------------------------------
/example/utils.ts:
--------------------------------------------------------------------------------
1 | import {Component} from 'react';
2 |
3 | export class ToolboxApp extends Component
{
4 | onCheckboxChange = (propName: any) => () => {
5 | const currentValue = (this.state as any)[propName];
6 | this.setState({ [propName]: !currentValue } as any);
7 | }
8 |
9 | onFieldTextChange = (propName: any) => (e: any) => {
10 | const value = e.target.value;
11 |
12 | (this as any).setState({
13 | [propName]: value
14 | });
15 | }
16 | }
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | modulePathIgnorePatterns: ['/dist'],
3 | preset: 'ts-jest',
4 | };
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack-imported",
3 | "version": "1.3.1",
4 | "description": "You chunks importing buddy",
5 | "main": "dist/es5/index.js",
6 | "module:es2019": "dist/es2019/index.js",
7 | "module": "dist/es2015/index.js",
8 | "types": "dist/es5/index.d.ts",
9 | "scripts": {
10 | "dev": "lib-builder dev",
11 | "test": "jest",
12 | "test:ci": "jest --runInBand --coverage",
13 | "build": "lib-builder build && yarn size:report",
14 | "release": "yarn build && yarn test",
15 | "size": "npx size-limit",
16 | "size:report": "npx size-limit --json > .size.json",
17 | "lint": "lib-builder lint",
18 | "format": "lib-builder format",
19 | "update": "lib-builder update",
20 | "prepublish": "yarn build && yarn changelog",
21 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
22 | "changelog:rewrite": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0"
23 | },
24 | "sideEffects": false,
25 | "repository": "https://github.com/theKashey/webpack-imported/",
26 | "author": "theKashey ",
27 | "license": "MIT",
28 | "resolutions": {
29 | "typescript": "^5.0.0"
30 | },
31 | "devDependencies": {
32 | "@size-limit/preset-small-lib": "^11.0.2",
33 | "size-limit": "^11.0.2",
34 | "@theuiteam/lib-builder": "^0.3.0",
35 | "@types/lodash": "^4.14.138",
36 | "webpack": "^5.28.5",
37 | "@types/node": "^18.0.0"
38 | },
39 | "engines": {
40 | "node": ">=10"
41 | },
42 | "files": [
43 | "dist",
44 | "css",
45 | "react"
46 | ],
47 | "keywords": [
48 | "webpack",
49 | "stat.json",
50 | "chunks",
51 | "codesplitting"
52 | ],
53 | "dependencies": {
54 | "kashe": "^1.0.3",
55 | "tslib": "^2.0.1"
56 | },
57 | "peerDependencies": {
58 | "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
59 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
60 | },
61 | "size-limit": [
62 | {
63 | "path": [
64 | "dist/es2015/react/index.js"
65 | ],
66 | "ignore": [
67 | "tslib",
68 | "react"
69 | ],
70 | "limit": "3 KB"
71 | }
72 | ],
73 | "peerDependenciesMeta": {
74 | "@types/react": {
75 | "optional": true
76 | }
77 | },
78 | "husky": {
79 | "hooks": {
80 | "pre-commit": "lint-staged"
81 | }
82 | },
83 | "lint-staged": {
84 | "*.{ts,tsx}": [
85 | "prettier --write",
86 | "eslint --fix",
87 | "git add"
88 | ],
89 | "*.{js,css,json,md}": [
90 | "prettier --write",
91 | "git add"
92 | ]
93 | },
94 | "prettier": {
95 | "printWidth": 120,
96 | "trailingComma": "es5",
97 | "tabWidth": 2,
98 | "semi": true,
99 | "singleQuote": true
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "main": "../dist/es5/react/index.js",
4 | "jsnext:main": "../dist/es2015/react/index.js",
5 | "module": "../dist/es2015/react/index.js",
6 | "types": "../dist/es2015/react/index.d.ts"
7 | }
8 |
--------------------------------------------------------------------------------
/src/css/correct-order.ts:
--------------------------------------------------------------------------------
1 | import { ImportedStat } from '../types';
2 | import { intentTheOrder } from '../utils/intent-the-order';
3 |
4 | export const stylesheetCorrectOrder = (
5 | /**
6 | * reference to `imported.stat`
7 | */
8 | stats: ImportedStat
9 | ) => {
10 | return intentTheOrder(...Object.values(stats.chunks).map(({ load }) => load));
11 | };
12 |
--------------------------------------------------------------------------------
/src/css/index.ts:
--------------------------------------------------------------------------------
1 | export { processImportedStyles } from './processImportedStyles';
2 | export { stylesheetCorrectOrder } from './correct-order';
3 |
--------------------------------------------------------------------------------
/src/css/processImportedStyles.ts:
--------------------------------------------------------------------------------
1 | let pendingPromises: Array> = [];
2 |
3 | export const processImportedStyles = (check?: boolean): Promise | undefined => {
4 | const links = Array.from(document.getElementsByTagName('style')).filter((link) => link.dataset.deferredStyle);
5 |
6 | if (check && !links.length && !pendingPromises.length) {
7 | return undefined;
8 | }
9 |
10 | const operation = Promise.all(
11 | links.map((link) => {
12 | if (link.dataset.deferredStyle) {
13 | const style = document.createElement('link');
14 | style.rel = 'stylesheet';
15 | style.href = link.dataset.href!;
16 | style.type = 'text/css';
17 |
18 | delete link.dataset.deferredStyle;
19 |
20 | return new Promise((resolve) => {
21 | style.addEventListener('load', resolve);
22 | link.parentNode!.insertBefore(style, link);
23 | link.parentNode!.removeChild(link);
24 | });
25 | }
26 |
27 | return null;
28 | })
29 | );
30 |
31 | pendingPromises.push(operation);
32 | operation.then(() => {
33 | pendingPromises = pendingPromises.filter((op) => op !== operation);
34 | });
35 |
36 | return Promise.all(pendingPromises);
37 | };
38 |
--------------------------------------------------------------------------------
/src/imported.spec.ts:
--------------------------------------------------------------------------------
1 | import { importAssets } from './imported';
2 | import { ImportedStat } from './types';
3 |
4 | describe('imported', () => {
5 | it('works', () => {
6 | const stats: ImportedStat = {
7 | config: {
8 | publicPath: 'publicPath',
9 | outputPath: 'outputPath',
10 | aliases: {},
11 | },
12 | chunks: {
13 | main: {
14 | load: [4],
15 | prefetch: [],
16 | preload: [],
17 | },
18 | a: {
19 | load: [1, 2, 4],
20 | prefetch: [],
21 | preload: [],
22 | },
23 | b: {
24 | load: [2, 3, 4],
25 | prefetch: [],
26 | preload: [],
27 | },
28 | },
29 | chunkMap: {
30 | 1: { js: ['1.js'] },
31 | 2: { js: ['2.js'], css: ['2.css'] },
32 | 3: { js: ['3.js'] },
33 | 4: { js: ['4.js'], css: ['3.css', '4.css'] },
34 | },
35 | assets: [],
36 | moduleMap: {},
37 | };
38 | expect(importAssets(stats, ['main'])).toMatchInlineSnapshot(`
39 | Object {
40 | "raw": Object {
41 | "load": Object {
42 | "css": Array [
43 | "3.css",
44 | "4.css",
45 | ],
46 | "js": Array [
47 | "4.js",
48 | ],
49 | },
50 | "prefetch": Object {},
51 | "preload": Object {},
52 | },
53 | "scripts": Object {
54 | "load": Array [
55 | "4.js",
56 | ],
57 | "prefetch": Array [],
58 | "preload": Array [],
59 | },
60 | "styles": Object {
61 | "load": Array [
62 | "3.css",
63 | "4.css",
64 | ],
65 | "prefetch": Array [],
66 | "preload": Array [],
67 | },
68 | }
69 | `);
70 | expect(importAssets(stats, ['main', 'a', 'b'])).toMatchInlineSnapshot(`
71 | Object {
72 | "raw": Object {
73 | "load": Object {
74 | "css": Array [
75 | "2.css",
76 | "3.css",
77 | "4.css",
78 | ],
79 | "js": Array [
80 | "1.js",
81 | "2.js",
82 | "3.js",
83 | "4.js",
84 | ],
85 | },
86 | "prefetch": Object {},
87 | "preload": Object {},
88 | },
89 | "scripts": Object {
90 | "load": Array [
91 | "1.js",
92 | "2.js",
93 | "3.js",
94 | "4.js",
95 | ],
96 | "prefetch": Array [],
97 | "preload": Array [],
98 | },
99 | "styles": Object {
100 | "load": Array [
101 | "2.css",
102 | "3.css",
103 | "4.css",
104 | ],
105 | "prefetch": Array [],
106 | "preload": Array [],
107 | },
108 | }
109 | `);
110 | });
111 | });
112 |
--------------------------------------------------------------------------------
/src/imported.ts:
--------------------------------------------------------------------------------
1 | import { kashe } from 'kashe';
2 |
3 | import { createImportedTracker } from './tracker';
4 | import {
5 | ChunkAsset,
6 | ChunkRef,
7 | ChunkRefs,
8 | Chunks,
9 | ImportedStat,
10 | ImportedTracker,
11 | RelatedAssets,
12 | RelatedImported,
13 | RelatedImportedPack,
14 | } from './types';
15 | import { intentTheOrder } from './utils/intent-the-order';
16 |
17 |
18 | const relatedToChunks = (
19 | importedStat: ImportedStat,
20 | tracker: ImportedTracker,
21 | initialChunkNames: string | string[]
22 | ): ImportedTracker => {
23 | const load: ChunkRefs = [];
24 | const preload: ChunkRefs = [];
25 | const prefetch: ChunkRefs = [];
26 |
27 | const chunkNames = Array.isArray(initialChunkNames) ? initialChunkNames : [initialChunkNames];
28 |
29 | chunkNames.forEach((chunkName) => {
30 | const chunk = importedStat.chunks[chunkName];
31 | if (!chunk) {
32 | throw new Error(`imported-stats: chunk "${chunkName}" was not found in stats.`);
33 | }
34 |
35 | chunk.load.forEach((chunkId) => {
36 | if (tracker.load.indexOf(chunkId) < 0) {
37 | tracker.load.push(chunkId);
38 | load.push(chunkId);
39 | }
40 | });
41 |
42 | chunk.preload.forEach((chunkId) => {
43 | if (tracker.preload.indexOf(chunkId) < 0) {
44 | tracker.preload.push(chunkId);
45 | preload.push(chunkId);
46 | }
47 | });
48 | chunk.prefetch.forEach((chunkId) => {
49 | if (tracker.prefetch.indexOf(chunkId) < 0) {
50 | tracker.prefetch.push(chunkId);
51 | prefetch.push(chunkId);
52 | }
53 | });
54 | });
55 |
56 | return {
57 | load,
58 | preload,
59 | prefetch,
60 | };
61 | };
62 |
63 | const flattenType = (types: ChunkAsset[]): Record => {
64 | const ret: Record = {};
65 | types.forEach((chunk) => {
66 | Object.keys(chunk).forEach((type) => {
67 | if (!(type in ret)) {
68 | ret[type] = [];
69 | }
70 | ret[type].push(...chunk[type]);
71 | });
72 | });
73 |
74 | return ret;
75 | };
76 |
77 | const importedScripts = (importedStep: RelatedImported): RelatedAssets => {
78 | const { load, preload, prefetch } = importedStep;
79 | return {
80 | load: load.js || [],
81 | preload: preload.js || [],
82 | prefetch: prefetch.js || [],
83 | };
84 | };
85 |
86 | const importedStyles = (importedStep: RelatedImported): RelatedAssets => {
87 | const { load, preload, prefetch } = importedStep;
88 |
89 | return {
90 | load: load.css || [],
91 | preload: preload.css || [],
92 | prefetch: prefetch.css || [],
93 | };
94 | };
95 |
96 | type OrderMap = Map;
97 | const cachedOrder = kashe(
98 | (chunks: Chunks): OrderMap => {
99 | const order: ChunkRefs = intentTheOrder(...Object.values(chunks).map(({ load }) => load));
100 | const map: OrderMap = new Map();
101 | order.forEach((x, index) => map.set(x, index));
102 | return map;
103 | }
104 | );
105 |
106 | const correctOrder = (order: OrderMap, load: ChunkRefs) => {
107 | load.sort((a, b) => order.get(a)! - order.get(b)!);
108 | return load;
109 | };
110 |
111 | export const importAssets = (
112 | importedStat: ImportedStat,
113 | chunkNames: string | string[],
114 | tracker: ImportedTracker = createImportedTracker()
115 | ): RelatedImportedPack => {
116 | const { load, preload, prefetch } = relatedToChunks(importedStat, tracker, chunkNames);
117 | const order = cachedOrder(importedStat.chunks);
118 | const raw: RelatedImported = {
119 | load: flattenType(correctOrder(order, load).map((chunkId) => importedStat.chunkMap[chunkId])),
120 | preload: flattenType(correctOrder(order, preload).map((chunkId) => importedStat.chunkMap[chunkId])),
121 | prefetch: flattenType(correctOrder(order, prefetch).map((chunkId) => importedStat.chunkMap[chunkId])),
122 | };
123 |
124 | return {
125 | raw,
126 | scripts: importedScripts(raw),
127 | styles: importedStyles(raw),
128 | };
129 | };
130 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export { ImportedPlugin, importStats } from './plugin';
2 | export { importAssets } from './imported';
3 | export type { ImportedStat } from './types';
4 |
--------------------------------------------------------------------------------
/src/plugin.ts:
--------------------------------------------------------------------------------
1 | import { writeFileSync } from 'fs';
2 | import { extname, relative } from 'path';
3 |
4 | import type { Compiler, Compilation, sources, StatsModule, StatsCompilation } from 'webpack';
5 |
6 | import type { ChunkMap, Chunks, ImportedStat, Asset } from './types';
7 |
8 | const merge = require('lodash/merge');
9 |
10 | type WebStats = Required<
11 | Pick
12 | >;
13 |
14 | const moduleToChunks = ({ chunks }: WebStats) => {
15 | const manifest: Record = {};
16 | chunks.forEach(({ id, modules }) => {
17 | if (modules) {
18 | modules.forEach((module: StatsModule) => {
19 | if (module.id && id) manifest[module.id] = id;
20 | });
21 | }
22 | });
23 |
24 | return manifest;
25 | };
26 |
27 | const getAssetType = (name: string) => extname(name).substr(1);
28 |
29 | const extractPrefetch = (prefetch: any[] = []): number[] =>
30 | Array.from(
31 | prefetch
32 | .map(({ chunks }) => chunks as number[])
33 | .reduce((acc, chunks) => {
34 | chunks.forEach((chunk) => acc.add(chunk));
35 |
36 | return acc;
37 | }, new Set())
38 | );
39 |
40 | const mapChunkNumbers = ({ assets }: WebStats): ChunkMap =>
41 | assets.reduce((acc, { name, chunks }) => {
42 | chunks?.forEach((chunk) => {
43 | acc[chunk] = acc[chunk] || {};
44 | const type = getAssetType(name);
45 | acc[chunk][type] = acc[chunk][type] || [];
46 | acc[chunk][type].push(name);
47 | });
48 |
49 | return acc;
50 | }, {} as ChunkMap);
51 |
52 | const getAssets = ({ assets }: WebStats): Asset[] =>
53 | assets!.map(({ name, size }) => ({ name, size, type: getAssetType(name) }));
54 |
55 | const getChunks = ({ namedChunkGroups }: WebStats): Chunks =>
56 | Object.keys(namedChunkGroups).reduce((acc, key) => {
57 | const { chunks, children } = namedChunkGroups[key];
58 | acc[key] = {
59 | load: chunks || [],
60 | preload: children?.preload ? extractPrefetch(children.preload) : [],
61 | prefetch: children?.prefetch ? extractPrefetch(children.prefetch) : [],
62 | };
63 |
64 | return acc;
65 | }, {} as Chunks);
66 |
67 | const resolveAliases = (cwd: string, aliases: Record): Record => {
68 | return Object.keys(aliases).reduce((acc, key) => {
69 | const alias = aliases[key];
70 | const paths = Array.isArray(alias) ? alias.map((aliasPath) => relative(cwd, aliasPath)) : relative(cwd, alias);
71 | return { ...acc, [key]: paths };
72 | }, {});
73 | };
74 |
75 | export const importStats = (stats: WebStats, extraProps: Record = {}): ImportedStat => {
76 | const cwd = process.cwd();
77 | const { publicPath, outputPath } = stats;
78 |
79 | return {
80 | config: {
81 | publicPath,
82 | outputPath: relative(cwd, outputPath),
83 | aliases: {},
84 | ...extraProps,
85 | },
86 |
87 | chunks: getChunks(stats),
88 | chunkMap: mapChunkNumbers(stats),
89 | assets: getAssets(stats),
90 | moduleMap: moduleToChunks(stats),
91 | };
92 | };
93 |
94 | interface Options {
95 | /**
96 | * bypasses webpack and saves file directly to the FS
97 | */
98 | saveToFile?: string;
99 | }
100 |
101 | /**
102 | * Webpack plugin
103 | */
104 | export class ImportedPlugin {
105 | constructor(private output: string, private options: Options = {}, private cache = {}) {}
106 |
107 | handleEmit = (compilation: Compilation) => {
108 | const stats = compilation.getStats().toJson({
109 | hash: true,
110 | publicPath: true,
111 | assets: true,
112 | chunks: true,
113 | modules: false,
114 | source: false,
115 | errorDetails: false,
116 | timings: false,
117 | }) as WebStats;
118 |
119 | const cwd = process.cwd();
120 | // not quite yet
121 | // const modules = compilation.options.resolve.modules;
122 | const aliases = resolveAliases(cwd, (compilation as any).options.resolve!.alias || {});
123 | // const {publicPath, outputPath} = stats;
124 |
125 | const result: ImportedStat = importStats(stats, { aliases });
126 |
127 | if (this.cache) {
128 | this.cache = merge(this.cache, result);
129 | }
130 |
131 | const stringResult = JSON.stringify(result, null, 2);
132 |
133 | if (this.options.saveToFile) {
134 | writeFileSync(this.options.saveToFile, stringResult);
135 | }
136 |
137 | if (this.output) {
138 | return {
139 | source() {
140 | return stringResult;
141 | },
142 | size() {
143 | return stringResult.length;
144 | },
145 | };
146 | }
147 |
148 | return null;
149 | };
150 |
151 | apply(compiler: Compiler) {
152 | const version = 'jsonpFunction' in compiler.options.output ? 4 : 5;
153 |
154 | if (version === 4) {
155 | compiler.hooks.emit.tap('ImportedPlugin', (compilation) => {
156 | const asset = this.handleEmit(compilation);
157 | if (asset) {
158 | compilation.assets[this.output] = asset as sources.Source;
159 | }
160 | });
161 | } else {
162 | compiler.hooks.make.tap('ImportedPlugin', (compilation) => {
163 | compilation.hooks.processAssets.tap(
164 | {
165 | name: 'ImportedPlugin',
166 | stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT,
167 | },
168 | () => {
169 | const asset = this.handleEmit(compilation);
170 | if (asset) {
171 | compilation.emitAsset(this.output, asset as sources.Source);
172 | }
173 | }
174 | );
175 | });
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/react/Atoms.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | type Omit = Pick>;
4 |
5 | export interface UnknownLink {
6 | href: string;
7 | as: 'script' | 'style' | 'font';
8 | type: string;
9 | anonymous?: boolean;
10 | }
11 |
12 | export type KnownLink = Omit;
13 | export type KnownTypedLink = Omit;
14 |
15 | const LinkPreload: React.FC = ({ href, as, type, anonymous, ...rest }) => (
16 |
17 | );
18 | const LinkPrefetch: React.FC = ({ href, as, type, anonymous, ...rest }) => (
19 |
20 | );
21 | export const PreloadFont: React.FC = ({ href, type, anonymous, ...rest }) => (
22 |
23 | );
24 | export const PrefetchFont: React.FC = ({ href, type, anonymous, ...rest }) => (
25 |
26 | );
27 | export const PrefetchStyle: React.FC = ({ href, ...rest }) => (
28 |
29 | );
30 | export const PreloadStyle: React.FC = ({ href, ...rest }) => (
31 |
32 | );
33 | export const PrefetchScript: React.FC = ({ href, ...rest }) => (
34 |
35 | );
36 | export const PreloadScript: React.FC = ({ href, ...rest }) => (
37 |
38 | );
39 |
--------------------------------------------------------------------------------
/src/react/Chunk.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { useContext } from 'react';
3 |
4 | import { importAssets } from '../imported';
5 | import { ImportedStat } from '../types';
6 | import { PrefetchScript, PrefetchStyle, PreloadScript, PreloadStyle } from './Atoms';
7 | import { LoadCriticalStyle, LoadScript, LoadStyle } from './Load';
8 | import { PrefetchChunkCollectorContext } from './context';
9 |
10 | export interface WebpackImportProps {
11 | /**
12 | * reference to `imported.stat`
13 | */
14 | stats: ImportedStat;
15 | /**
16 | * list of chunks to load
17 | */
18 | chunks: string | string[];
19 | /**
20 | * should prefetch or preload be used
21 | */
22 | scriptsHint?: 'prefetch' | 'preload';
23 | /**
24 | * should scripts be loaded as anonymous
25 | */
26 | anonymous?: boolean;
27 | /**
28 | * should scripts be loaded as async (default is defer)
29 | */
30 | async?: boolean;
31 | /**
32 | * should scripts be loaded as ESM modules
33 | */
34 | module?: boolean;
35 | /**
36 | * should found CSS files be considered as critical and NOT loaded
37 | * and if yes - should they be prefetched or preloaded (or nothing)
38 | */
39 | criticalCSS?: boolean | 'prefetch' | 'preload';
40 | /**
41 | * public path for all assets
42 | */
43 | publicPath?: string;
44 | }
45 |
46 | /**
47 | * Preloads all given chunks
48 | * @example
49 | * = 0}
53 | * scriptsHint={priority >= 1 ? 'preload' : 'prefetch'}
54 | * criticalCSS="prefetch"
55 | * publicPath={`${CDN}${data.config.publicPath}${mode}/`}
56 | * />
57 | */
58 | export const WebpackImport: React.FC = ({
59 | stats,
60 | chunks,
61 | scriptsHint,
62 | criticalCSS,
63 | anonymous,
64 | async = true,
65 | module,
66 | publicPath = stats.config.publicPath,
67 | }) => {
68 | const tracker = useContext(PrefetchChunkCollectorContext);
69 | const { scripts, styles } = importAssets(stats, chunks, tracker);
70 |
71 | return (
72 | <>
73 | {scripts.load.map((asset) => (
74 |
75 | {scriptsHint === 'prefetch' && }
76 | {scriptsHint === 'preload' && }
77 |
78 |
79 | ))}
80 | {scripts.preload.map((asset) => (
81 |
82 | ))}
83 | {scripts.prefetch.map((asset) => (
84 |
85 | ))}
86 |
87 | {styles.load.map((asset) => (
88 |
89 | {criticalCSS ? (
90 | <>
91 | {criticalCSS === 'prefetch' && }
92 | {criticalCSS === 'preload' && }
93 |
94 | >
95 | ) : (
96 |
97 | )}
98 |
99 | ))}
100 | >
101 | );
102 | };
103 |
104 | export interface WebpackPreloadProps {
105 | /**
106 | * reference to `imported.stat`
107 | */
108 | stats: ImportedStat;
109 | /**
110 | * list of chunks to load
111 | */
112 | chunks: string | string[];
113 | /**
114 | * should prefetch or preload be used
115 | */
116 | scriptsHint: 'prefetch' | 'preload';
117 | /**
118 | * should prefetch or preload be used
119 | */
120 | stylesHint: 'prefetch' | 'preload';
121 | /**
122 | * suppresses styles for the initial load as they are expected to be a part of critical CSS
123 | * styles will be placed in the correct order by calling {@link processImportedStyles}
124 | */
125 | criticalCSS?: boolean;
126 | /**
127 | * should scripts be loaded as anonymous
128 | */
129 | anonymous?: boolean;
130 | /**
131 | * public path for all assets
132 | */
133 | publicPath?: string;
134 | }
135 |
136 | /**
137 | * Preloads(or prefetches) all given chunks
138 | *
139 | * @example
140 | *
147 | */
148 | export const WebpackPreload: React.FC = ({
149 | stats,
150 | chunks,
151 | scriptsHint,
152 | stylesHint,
153 | criticalCSS,
154 | anonymous,
155 | publicPath = stats.config.publicPath,
156 | }) => {
157 | const tracker = useContext(PrefetchChunkCollectorContext);
158 | const { scripts, styles } = importAssets(stats, chunks, tracker);
159 |
160 | return (
161 | <>
162 | {scripts.load.map((asset) => (
163 |
164 | {scriptsHint === 'prefetch' ? (
165 |
166 | ) : (
167 |
168 | )}
169 |
170 | ))}
171 | {styles.load.map((asset) => (
172 |
173 | {stylesHint === 'prefetch' ? (
174 |
175 | ) : (
176 |
177 | )}
178 | {criticalCSS && }
179 |
180 | ))}
181 | >
182 | );
183 | };
184 |
--------------------------------------------------------------------------------
/src/react/Load.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export interface KnownScript {
4 | href: string;
5 | anonymous?: boolean;
6 | async?: boolean;
7 | module?: boolean;
8 | }
9 |
10 | export const LoadScript: React.FC = ({ href, anonymous, async = true, module }) => (
11 |
18 | );
19 |
20 | export interface KnownStyle {
21 | href: string;
22 | }
23 |
24 | export const LoadStyle: React.FC = ({ href }) => ;
25 |
26 | export const LoadCriticalStyle: React.FC = ({ href }) => ;
27 |
--------------------------------------------------------------------------------
/src/react/context.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import type {FC, PropsWithChildren} from "react";
4 |
5 | import {createImportedTracker} from "../tracker";
6 | import type {ImportedTracker} from "../types";
7 |
8 | export const PrefetchChunkCollectorContext = React.createContext(createImportedTracker());
9 |
10 | export const WebpackImportedProvider: FC> = ({children, tracker = createImportedTracker()}) => (
13 |
14 | {children}
15 |
16 | );
--------------------------------------------------------------------------------
/src/react/index.ts:
--------------------------------------------------------------------------------
1 | export {createImportedTracker} from "../tracker";
2 | export {WebpackImportedProvider} from './context';
3 | export {WebpackImport, WebpackPreload} from './Chunk';
4 | export {LoadStyle, LoadScript, LoadCriticalStyle} from './Load';
5 | export {processImportedStyles} from '../css/processImportedStyles'
6 | export {PrefetchScript, PreloadScript, PrefetchFont, PreloadFont, PrefetchStyle, PreloadStyle} from "./Atoms";
--------------------------------------------------------------------------------
/src/tracker.ts:
--------------------------------------------------------------------------------
1 | import {ImportedTracker} from "./types";
2 |
3 | /**
4 | * creates a tracker - structure which holds information of referenced assets
5 | * Used to prevent resource duplication
6 | * @see {@link https://github.com/theKashey/webpack-imported#ssr-api}
7 | * @example
8 | * const tracker = createImportedTracker();
9 | * const relatedAssets1 = importedAssets(importedStat, ['main'], tracker);
10 | */
11 | export const createImportedTracker = (): ImportedTracker => ({
12 | load: [],
13 | preload: [],
14 | prefetch: [],
15 | });
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | export type ChunkRef = number | string;
2 | export type ChunkRefs = ChunkRef[];
3 |
4 | export interface Chunk {
5 | load: ChunkRefs;
6 | preload: ChunkRefs;
7 | prefetch: ChunkRefs;
8 | }
9 |
10 | export interface Asset {
11 | name: string;
12 | size: number;
13 | type: string;
14 | }
15 |
16 | export type ChunkAsset = Record;
17 |
18 | export type Chunks = Record;
19 | export type ChunkMap = Record;
20 |
21 | /**
22 | * Structure representing the saved file
23 | */
24 | export interface ImportedStat {
25 | config: {
26 | publicPath: string;
27 | outputPath: string;
28 | aliases: Record;
29 | };
30 | chunks: Chunks;
31 | chunkMap: ChunkMap;
32 | assets: Asset[];
33 | moduleMap: Record;
34 | }
35 |
36 | export interface ImportedTracker {
37 | load: ChunkRefs;
38 | preload: ChunkRefs;
39 | prefetch: ChunkRefs;
40 | }
41 |
42 | export interface RelatedAssets {
43 | load: string[];
44 | preload: string[];
45 | prefetch: string[];
46 | }
47 |
48 | export interface RelatedImported {
49 | load: Record;
50 | preload: Record;
51 | prefetch: Record;
52 | }
53 |
54 | export interface RelatedImportedPack {
55 | raw: RelatedImported;
56 | scripts: RelatedAssets;
57 | styles: RelatedAssets;
58 | }
59 |
--------------------------------------------------------------------------------
/src/utils/intent-the-order.spec.ts:
--------------------------------------------------------------------------------
1 | import { intentTheOrder } from './intent-the-order';
2 |
3 | describe('css-order', () => {
4 | it('merges simple order', () => {
5 | expect(intentTheOrder([], [1, 2, 3])).toEqual([1, 2, 3]);
6 | });
7 |
8 | it('merges overlapping order', () => {
9 | expect(intentTheOrder([1, 3], [1, 2, 3])).toEqual([1, 2, 3]);
10 | });
11 |
12 | it('merges not overlapping order', () => {
13 | expect(intentTheOrder([1, 4], [1, 2, 3])).toEqual([1, 4, 2, 3]);
14 | });
15 |
16 | it('merges not overlapping order with extra info', () => {
17 | expect(intentTheOrder([1, 4], [1, 2, 3], [3, 4])).toEqual([1, 2, 3, 4]);
18 | });
19 |
20 | it('merges complex case', () => {
21 | expect(intentTheOrder(['start'], [1, 4], ['gap'], ['tail'], [1, 2, 3], [3, 4], ['end', 'tail'], ['end'])).toEqual([
22 | 'start',
23 | 1,
24 | 2,
25 | 3,
26 | 4,
27 | 'gap',
28 | 'end',
29 | 'tail',
30 | ]);
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/src/utils/intent-the-order.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-param-reassign */
2 | type PairMap = Record>;
3 |
4 | const putPair = (map: PairMap, a: any, b: any) => {
5 | if (!('a' in map)) {
6 | map[a] = {};
7 | }
8 | map[a][b] = true;
9 | };
10 |
11 | const getPair = (map: PairMap, a: any, b: any) => {
12 | return map[a] ? map[a][b] : undefined;
13 | };
14 |
15 | const getPairDeep = (map: PairMap, a: any, b: any): boolean => {
16 | if (getPair(map, a, b)) {
17 | return true;
18 | }
19 | const variants = map[a];
20 | return variants && Object.keys(variants).some((x) => getPairDeep(map, x, b));
21 | };
22 |
23 | /**
24 | * restores the original order of pieces presented in different subsets
25 | * @param sets
26 | */
27 | export const intentTheOrder = (...sets: any[][]): any[] => {
28 | const result: any[] = [];
29 | const map: PairMap = {};
30 | const knownValues = new Set();
31 |
32 | // create a {
34 | for (let i = 0; i < set.length - 1; i += 1) {
35 | putPair(map, set[i], set[i + 1]);
36 | knownValues.add(set[i]);
37 | }
38 | });
39 |
40 | const savedValues = new Set();
41 |
42 | const push = (v: any) => {
43 | if (savedValues.has(v)) {
44 | return;
45 | }
46 | savedValues.add(v);
47 |
48 | for (let index = 0; index <= result.length; index += 1) {
49 | const mapv = result[index];
50 | // v is known to be < mapv
51 | if (getPairDeep(map, v, mapv)) {
52 | result.splice(index, 0, v);
53 | return;
54 | }
55 | }
56 | result.push(v);
57 | };
58 | sets.forEach((set) => set.forEach((x) => push(x)));
59 |
60 | return result;
61 | };
62 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": true,
4 | "allowSyntheticDefaultImports": true,
5 | "strict": true,
6 | "strictNullChecks": true,
7 | "strictFunctionTypes": true,
8 | "noImplicitThis": true,
9 | "alwaysStrict": true,
10 | "noUnusedLocals": true,
11 | "noUnusedParameters": true,
12 | "noImplicitReturns": true,
13 | "noFallthroughCasesInSwitch": true,
14 | "noImplicitAny": true,
15 | "importHelpers": true,
16 | "isolatedModules": true,
17 | "target": "es6",
18 | "moduleResolution": "node",
19 | "lib": ["dom", "es5", "scripthost", "es2015.collection", "es2015.symbol", "es2015.iterable", "es2015.promise"],
20 | "types": ["node", "jest"],
21 | "typeRoots": ["./node_modules/@types"],
22 | "jsx": "react"
23 | },
24 | "exclude": ["node_modules"]
25 | }
26 |
--------------------------------------------------------------------------------