├── .babelrc ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── main.yml ├── .gitignore ├── .npmrc ├── .nvmrc ├── .travis.yml ├── appveyor.yml ├── changelog.md ├── code_of_conduct.md ├── contributing.md ├── index.js ├── lib ├── getAssetKind.js ├── getDynamicImportedChildAssets.js ├── getFileExtension.js ├── isHMRUpdate.js ├── isSourceMap.js ├── output │ ├── createOutputWriter.js │ └── createQueuedWriter.js ├── pathTemplate.js └── utils │ └── error.js ├── license.md ├── package.json ├── pull_request_template.md ├── readme.md ├── renovate.json └── test ├── .eslintrc ├── fixtures ├── common-chunks │ ├── common.js │ ├── one.js │ └── two.js ├── manifest.js ├── manifestWithSourceMap.js ├── one.js ├── styles.js ├── stylesheet1.css ├── stylesheet2.css └── two.js ├── getAssetKind.test.js ├── getFileExt.test.js ├── index.test.js ├── isHMRUpdate.test.js ├── isSourceMap.test.js ├── multiCompiler.test.js ├── pathTemplate.test.js └── utils └── expectOutput.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | insert_final_newline = true 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | end_of_line = lf 8 | 9 | [*.{js,json}] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [Makefile] 14 | indent_style = tab 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Webpack Config** 21 | The webpack configuration you are using when seeing the bug. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: 25 | - Node version: 26 | - Plugin version: 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Create Tag 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: Klemensas/action-autotag@stable 14 | with: 15 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | tmp 4 | npm-debug.log* 5 | .package.json 6 | 7 | # Lock files shouldn't be committed for libraries https://stackoverflow.com/a/40206145 8 | yarn.lock 9 | package-lock.json 10 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v14 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "10" 5 | 6 | script: 7 | - npm run lint 8 | - npm run test 9 | 10 | cache: 11 | directories: 12 | - node_modules 13 | 14 | before_deploy: 15 | - git config --local user.name "ztoben" 16 | - git config --local user.email "zach.toben@gmail.com" 17 | deploy: 18 | provider: releases 19 | api_key: $GH_TOKEN 20 | skip_cleanup: true 21 | on: 22 | branch: master 23 | tags: true 24 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | nodejs_version: "10" 3 | 4 | platform: 5 | - x64 6 | 7 | install: 8 | - ps: Install-Product node $env:nodejs_version 9 | - npm install 10 | 11 | test_script: 12 | - npm test 13 | 14 | # Disable missing build step 15 | build: off 16 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | This project adheres to [Semantic Versioning]. 5 | 6 | [Semantic Versioning]: http://semver.org/ 7 | 8 | ## 7.1.1 - 2021-07-08 9 | * Bump lodash for security concerns 10 | 11 | ## 7.1.0 - 2020-12-11 12 | * Closes [#327](https://github.com/ztoben/assets-webpack-plugin/issues/327) 13 | 14 | ## 7.0.0 - 2020-12-11 15 | * Requires webpack 5 or greater as a peer dependency 16 | * Closes [#322](https://github.com/ztoben/assets-webpack-plugin/issues/322) 17 | 18 | ## 6.1.2 - 2020-12-08 19 | * Closes [#319](https://github.com/ztoben/assets-webpack-plugin/issues/319) 20 | 21 | ## 6.1.1 - 2020-12-03 22 | * Adds the `includeDynamicImportedAssets` option 23 | * Closes [#313](https://github.com/ztoben/assets-webpack-plugin/issues/313) 24 | 25 | ## 6.1.0 - 2020-12-03 26 | * Closes [#312](https://github.com/ztoben/assets-webpack-plugin/issues/312) 27 | 28 | ## 6.0.6 - 2020-12-02 29 | * Adds [#311](https://github.com/ztoben/assets-webpack-plugin/pull/311) 30 | * Fixes issue where default path would not be set 31 | 32 | ## 6.0.5 - 2020-12-01 33 | * Adds [#310](https://github.com/ztoben/assets-webpack-plugin/pull/310) 34 | 35 | ## 6.0.4 - 2020-11-13 36 | * Adds the `includeAuxiliaryAssets` option 37 | * Closes [#302](https://github.com/ztoben/assets-webpack-plugin/issues/302) 38 | 39 | ## 6.0.2 - 2020-10-16 40 | * Closes [#279](https://github.com/ztoben/assets-webpack-plugin/issues/279) 41 | 42 | ## 6.0.1 - 2020-10-15 43 | * Initial webpack 5 support 44 | * Versions going forward will support webpack 5 only 45 | 46 | ## 6.0.0 47 | * This release is deprecated. 48 | 49 | ## 5.1.2 - 2020-09-22 50 | 51 | * Add a new option to include assets that aren't in any chunk using the `includeFilesWithoutChunk` option. 52 | * Closes [#204](https://github.com/ztoben/assets-webpack-plugin/issues/204) 53 | 54 | ## 5.1.1 - 2020-08-17 55 | 56 | * Allow more than one entry in `includeManifest`. 57 | * Closes [#84](https://github.com/ztoben/assets-webpack-plugin/issues/84) 58 | 59 | ## 5.1.0 - 2020-08-17 60 | 61 | * Implements using more relaxed dependencies. 62 | * Fixes [#240](https://github.com/ztoben/assets-webpack-plugin/issues/240) 63 | 64 | ## 5.0.5 - 2020-08-17 65 | 66 | * Plugin will now parse the `.mjs` file extension. 67 | 68 | ## 5.0.4 - 2020-08-14 69 | 70 | * Fixes [#251](https://github.com/ztoben/assets-webpack-plugin/issues/251) 71 | 72 | ## 5.0.3 - 2020-07-09 73 | 74 | * Bumps several dependencies. 75 | 76 | ## 5.0.2 - 2020-06-15 77 | 78 | * Fixes an error when `keepInMemory` was turn on. 79 | 80 | ## 5.0.1 - 2020-06-15 81 | 82 | * Fixes [#234](https://github.com/ztoben/assets-webpack-plugin/issues/234) 83 | 84 | ## 5.0.0 - 2020-06-11 85 | 86 | * Unfortunately must bump to v5 instead of v4 due to an old botched release that can't be unpublished 87 | * Major bump due to now requiring node 10 88 | * Maintains the same changes as `3.10.0`, but properly warns when using an older version of node 89 | 90 | ## 3.10.0 - 2020-06-08 (Deprecated) 91 | 92 | * Updates several dependencies 93 | * Fixes [#226](https://github.com/ztoben/assets-webpack-plugin/issues/226) 94 | 95 | ## 3.9.10 - 2019-02-28 96 | 97 | * Fixes [#169](https://github.com/ztoben/assets-webpack-plugin/issues/169) 98 | 99 | ## 3.9.9 - 2019-02-28 100 | 101 | * Adds the `integrity` option to add output from webpack-subresource-integrity to the json file 102 | * Fixes [#63](https://github.com/ztoben/assets-webpack-plugin/issues/63) 103 | 104 | ## 3.9.8 - 2019-02-27 105 | 106 | * Dependency updates 107 | * Fixes [#167](https://github.com/ztoben/assets-webpack-plugin/issues/167) 108 | 109 | ## 3.9.7 - 2018-10-04 110 | 111 | * Allow webpack 4 entrypoints chunks 112 | * Fixes [#108](https://github.com/ztoben/assets-webpack-plugin/issues/108) 113 | 114 | ## 3.9.6 - 2018-08-20 115 | 116 | * Fixes [#125](https://github.com/ztoben/assets-webpack-plugin/issues/125) 117 | 118 | ## 3.9.5 - 2018-08-09 119 | 120 | * Adds the `keepInMemory` option to toggle whether you want the assets file generated when running in `webpack-dev-server`. 121 | 122 | ## 3.9.4 - 2018-08-08 123 | 124 | * Adds the `includeAllFileTypes`, and `fileTypes` options for controlling which files are included in the assets file. 125 | 126 | ## 3.9.3 - 2018-08-07 127 | 128 | * Fixes an issue where `useCompilerPath` wasn't correctly resolving the path. 129 | 130 | ## 3.9.2 - 2018-08-07 131 | 132 | * Reverts [#109](https://github.com/ztoben/assets-webpack-plugin/pull/109), fixes [#118](https://github.com/ztoben/assets-webpack-plugin/issues/118). 133 | 134 | ## 3.9.1 - 2018-08-06 135 | 136 | * `useCompilerPath` option to override path with webpack output path set in config. 137 | 138 | ## 3.9.0 - 2018-08-06 139 | 140 | * ~~Now supports webpack 4 entries with multiple chunks. See [#109](https://github.com/ztoben/assets-webpack-plugin/pull/109) for details.~~ 141 | * Use compiler.outputFileSystem for output. 142 | * Fixes [#108](https://github.com/ztoben/assets-webpack-plugin/issues/108), [#111](https://github.com/ztoben/assets-webpack-plugin/issues/111), and [#92](https://github.com/ztoben/assets-webpack-plugin/issues/92). 143 | 144 | ## 3.8.4 - 2018-06-20 145 | 146 | ### Changed 147 | 148 | * No code changed. Purely for testing tagged releases on git. 149 | 150 | ## 3.8.3 - 2018-06-18 151 | 152 | ### Changed 153 | 154 | * Package json was pointing to the wrong index file. 155 | 156 | ## 3.8.2 - 2018-06-18 157 | 158 | ### Changed 159 | 160 | * Add babel to the build process. 161 | 162 | ## 3.8.1 - 2018-06-18 163 | 164 | ### Changed 165 | 166 | * Support for listing the manifest entry first when `manifestFirst` option is set. See [#66](https://github.com/ztoben/assets-webpack-plugin/issues/66) for details. 167 | 168 | ## 3.8.0 - 2018-06-15 169 | 170 | ### Changed 171 | 172 | * Reverts [#90](https://github.com/ztoben/assets-webpack-plugin/pull/90), fixes [#93](https://github.com/ztoben/assets-webpack-plugin/issues/93) and [#92](https://github.com/ztoben/assets-webpack-plugin/issues/92) 173 | 174 | ## 3.7.2 - 2018-06-14 175 | 176 | ### Changed 177 | 178 | * Reduces npm package size [#67](https://github.com/ztoben/assets-webpack-plugin/issues/67) 179 | 180 | ## 3.7.1 - 2018-06-13 181 | 182 | ### Changed 183 | 184 | * Fixes a parsing error with the asset path introduced by the fix for [#88](https://github.com/ztoben/assets-webpack-plugin/issues/88) 185 | 186 | ## 3.7.0 - 2018-06-13 187 | 188 | ### Changed 189 | 190 | * Adds all assets to the manifest that aren't in a chunk (kudos to [@Kronuz](https://github.com/Kronuz) see [#65](https://github.com/ztoben/assets-webpack-plugin/pull/65)) 191 | 192 | ## 3.6.3 - 2018-06-13 193 | 194 | ### Changed 195 | 196 | * Add support for multiple files of the same extension (kudos to [@aaronatmycujoo](https://github.com/aaronatmycujoo) see [#79](https://github.com/ztoben/assets-webpack-plugin/pull/79) and [@Kronuz](https://github.com/Kronuz) see [#64](https://github.com/ztoben/assets-webpack-plugin/pull/64)) 197 | 198 | ## 3.6.2 - 2018-06-13 199 | 200 | ### Changed 201 | 202 | * Fixed incorrect concatination of asset file names and directory path see [#88](https://github.com/ztoben/assets-webpack-plugin/issues/88) 203 | 204 | ## 3.6.1 - 2018-06-13 205 | 206 | ### Changed 207 | 208 | * webpack-dev-server (which uses memory-fs) correctly generates the manifest inside the memory file system (kudos to [@Kronuz](https://github.com/Kronuz) see [#90](https://github.com/ztoben/assets-webpack-plugin/pull/90)) 209 | 210 | ## 3.6.0 - 2018-05-29 211 | 212 | ### Changed 213 | 214 | * webpack 4 support (kudos to [@ztoben](https://github.com/ztoben) and [@saveman71](https://github.com/saveman71) see [#89](https://github.com/ztoben/assets-webpack-plugin/pull/89)) 215 | 216 | ## 3.5.1 - 2017-01-20 217 | 218 | ### Fixed 219 | 220 | * Support for source maps when `includeManifest` option is set. 221 | 222 | ## 3.5.0 - 2016-10-21 223 | 224 | ### Added 225 | 226 | * `includeManifest` option (kudos to Matt Krick [@mattkrick](https://github.com/mattkrick)). 227 | [See docs](./README.md#includemanifest) for more details. 228 | 229 | ## 3.4.0 - 2016-03-09 230 | 231 | ### Changed 232 | 233 | * Do not write to assets file if output hasn't changed. 234 | 235 | ## 3.2.0 - 2015-11-17 236 | 237 | ### Added 238 | 239 | * `processOutput` option. 240 | 241 | ## 3.1.0 - 2015-10-24 242 | 243 | ### Added 244 | 245 | * Config now accepts a `fullPath` option. 246 | -------------------------------------------------------------------------------- /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 contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [zach.toben@gmail.com.](mailto:zach.toben@gmail.com.) The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | 47 | [version]: http://contributor-covenant.org/version/1/4/ 48 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | ## Table of Contents 4 | 5 | * [Contributing Guidelines](#contributing-guidelines) 6 | * [Code Style Guide](#code-style-guide) 7 | * [Lint the Code](#lint-the-code) 8 | * [Use EditorConfig](#use-editorconfig) 9 | 10 | ## Contributing Guidelines 11 | 12 | Here is some advice on how to craft a pull request with the best possible 13 | chance of being accepted: 14 | 15 | * [Follow code style guides](#code-style-guide). 16 | 17 | * Write tests. 18 | 19 | * Write documentation. 20 | 21 | * [Write good commit messages]. 22 | 23 | * Don't change the library version. 24 | 25 | * Add an entry to [Unreleased section in CHANGELOG]. 26 | 27 | * [Squash related commits] when the PR is ready to merge. 28 | 29 | [Write good commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 30 | 31 | [Unreleased section in CHANGELOG]: https://github.com/ztoben/assets-webpack-plugin/blob/master/CHANGELOG.md#unreleased 32 | 33 | [Squash related commits]: https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Squashing-Commits 34 | 35 | ## Code Style Guide 36 | 37 | ### Lint the Code 38 | 39 | The project follows [JavaScript Standard Style]. To lint the code, run: 40 | 41 | ```bash 42 | npm run lint 43 | ``` 44 | 45 | [JavaScript Standard Style]: http://standardjs.com/ 46 | 47 | ### Use EditorConfig 48 | 49 | The project uses [EditorConfig] to define basic coding style guides. 50 | Please install a plugin for your editor of choice or manually enforce 51 | the rules listed in [.editorconfig]. 52 | 53 | [EditorConfig]: http://editorconfig.org 54 | 55 | [.editorconfig]: https://github.com/ztoben/assets-webpack-plugin/blob/master/.editorconfig 56 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const _ = require('lodash') 4 | 5 | const getAssetKind = require('./lib/getAssetKind') 6 | const isHMRUpdate = require('./lib/isHMRUpdate') 7 | const isSourceMap = require('./lib/isSourceMap') 8 | const getDynamicImportedChildAssets = require('./lib/getDynamicImportedChildAssets') 9 | 10 | const createQueuedWriter = require('./lib/output/createQueuedWriter') 11 | const createOutputWriter = require('./lib/output/createOutputWriter') 12 | 13 | function AssetsWebpackPlugin (options) { 14 | this.options = _.merge({}, { 15 | filename: 'webpack-assets.json', 16 | prettyPrint: false, 17 | update: false, 18 | fullPath: true, 19 | manifestFirst: true, 20 | useCompilerPath: false, 21 | fileTypes: ['js', 'css'], 22 | includeAllFileTypes: true, 23 | includeFilesWithoutChunk: false, 24 | includeAuxiliaryAssets: false, 25 | includeDynamicImportedAssets: false, 26 | keepInMemory: false, 27 | integrity: false, 28 | removeFullPathAutoPrefix: false 29 | }, options) 30 | } 31 | 32 | AssetsWebpackPlugin.prototype = { 33 | constructor: AssetsWebpackPlugin, 34 | 35 | apply: function (compiler) { 36 | const self = this 37 | 38 | self.options.path = path.resolve( 39 | self.options.useCompilerPath 40 | ? (compiler.options.output.path || '.') 41 | : (self.options.path || '.') 42 | ) 43 | self.writer = createQueuedWriter(createOutputWriter(self.options)) 44 | 45 | const emitPlugin = (compilation, callback) => { 46 | const options = compiler.options 47 | const stats = compilation.getStats().toJson({ 48 | hash: true, 49 | publicPath: true, 50 | assets: true, 51 | chunks: false, 52 | modules: false, 53 | source: false, 54 | errorDetails: false, 55 | timings: false 56 | }) 57 | 58 | let assetPath = (stats.publicPath && self.options.fullPath) ? stats.publicPath : '' 59 | // assetsByChunkName contains a hash with the bundle names and the produced files 60 | // e.g. { one: 'one-bundle.js', two: 'two-bundle.js' } 61 | // in some cases (when using a plugin or source maps) it might contain an array of produced files 62 | // e.g. { 63 | // main: 64 | // [ 'index-bundle-42b6e1ec4fa8c5f0303e.js', 65 | // 'index-bundle-42b6e1ec4fa8c5f0303e.js.map' ] 66 | // } 67 | // starting with webpack 5, the public path is automatically determined when possible and the path is prefaced 68 | // with `/auto/`, the `removeAutoPrefix` option can be set to turn this off 69 | 70 | if (self.options.removeFullPathAutoPrefix) { 71 | if (assetPath.startsWith('auto')) { 72 | assetPath = assetPath.substring(4) 73 | } 74 | } 75 | 76 | const seenAssets = {} 77 | let chunks 78 | 79 | if (self.options.entrypoints) { 80 | chunks = Object.keys(stats.entrypoints) 81 | if (self.options.includeFilesWithoutChunk) { 82 | chunks.push('') // push "unnamed" chunk 83 | } 84 | } else { 85 | chunks = Object.keys(stats.assetsByChunkName) 86 | chunks.push('') // push "unnamed" chunk 87 | } 88 | 89 | const output = chunks.reduce(function (chunkMap, chunkName) { 90 | let assets 91 | 92 | if (self.options.entrypoints) { 93 | assets = chunkName ? stats.entrypoints[chunkName].assets : stats.assets 94 | } else { 95 | assets = chunkName ? stats.assetsByChunkName[chunkName] : stats.assets 96 | } 97 | 98 | if (self.options.includeAuxiliaryAssets && chunkName && stats.entrypoints[chunkName].auxiliaryAssets) { 99 | assets = [...assets, ...stats.entrypoints[chunkName].auxiliaryAssets] 100 | } 101 | 102 | if (self.options.includeDynamicImportedAssets && chunkName && stats.entrypoints[chunkName].children) { 103 | const dynamicImportedChildAssets = getDynamicImportedChildAssets(options, stats.entrypoints[chunkName].children) 104 | assets = [...assets, ...dynamicImportedChildAssets] 105 | } 106 | 107 | if (!Array.isArray(assets)) { 108 | assets = [assets] 109 | } 110 | let added = false 111 | const typeMap = assets.reduce(function (typeMap, obj) { 112 | const asset = obj.name || obj 113 | 114 | if (isHMRUpdate(options, asset) || isSourceMap(options, asset) || (!chunkName && seenAssets[asset])) { 115 | return typeMap 116 | } 117 | 118 | const typeName = getAssetKind(options, asset) 119 | if (self.options.includeAllFileTypes || self.options.fileTypes.includes(typeName)) { 120 | const combinedPath = assetPath && assetPath.slice(-1) !== '/' ? `${assetPath}/${asset}` : assetPath + asset 121 | const type = typeof typeMap[typeName] 122 | const compilationAsset = compilation.assets[asset] 123 | const integrity = compilationAsset && compilationAsset.integrity 124 | const loadingBehavior = obj.loadingBehavior 125 | 126 | if (type === 'undefined') { 127 | typeMap[typeName] = combinedPath 128 | 129 | if (self.options.integrity && integrity) { 130 | typeMap[typeName + 'Integrity'] = integrity 131 | } 132 | } else { 133 | if (type === 'string') { 134 | typeMap[typeName] = [typeMap[typeName]] 135 | } 136 | 137 | if (self.options.includeDynamicImportedAssets && loadingBehavior) { 138 | const typeNameWithLoadingBehavior = typeName + ':' + loadingBehavior 139 | 140 | typeMap[typeNameWithLoadingBehavior] = typeMap[typeNameWithLoadingBehavior] || [] 141 | typeMap[typeNameWithLoadingBehavior].push(combinedPath) 142 | } else { 143 | typeMap[typeName].push(combinedPath) 144 | } 145 | } 146 | 147 | added = true 148 | seenAssets[asset] = true 149 | } 150 | return typeMap 151 | }, {}) 152 | 153 | if (added) { 154 | chunkMap[chunkName] = typeMap 155 | } 156 | return chunkMap 157 | }, {}) 158 | 159 | let manifestNames = self.options.includeManifest === true ? ['manifest'] : self.options.includeManifest 160 | 161 | if (typeof manifestNames === 'string') { 162 | manifestNames = [manifestNames] 163 | } 164 | 165 | if (manifestNames) { 166 | for (let i = 0; i < manifestNames.length; i++) { 167 | const manifestName = manifestNames[i] 168 | const manifestEntry = output[manifestName] 169 | 170 | if (manifestEntry) { 171 | let js = manifestEntry.js || manifestEntry.mjs 172 | if (!Array.isArray(js)) { 173 | js = [js] 174 | } 175 | const manifestAssetKey = js[js.length - 1].substr(assetPath.length) 176 | const parentSource = compilation.assets[manifestAssetKey] 177 | const entryText = parentSource.source() 178 | if (!entryText) { 179 | throw new Error('Could not locate manifest function in source', parentSource) 180 | } 181 | manifestEntry.text = entryText 182 | } 183 | } 184 | } 185 | 186 | if (self.options.metadata) { 187 | output.metadata = self.options.metadata 188 | } 189 | 190 | if (!compiler.outputFileSystem.readFile) { 191 | compiler.outputFileSystem.readFile = fs.readFile.bind(fs) 192 | } 193 | 194 | if (!compiler.outputFileSystem.join) { 195 | compiler.outputFileSystem.join = path.join.bind(path) 196 | } 197 | 198 | self.writer(compiler.outputFileSystem, output, function (err) { 199 | if (err) { 200 | compilation.errors.push(err) 201 | } 202 | callback() 203 | }) 204 | } 205 | 206 | if (compiler.hooks) { 207 | const plugin = { name: 'AssetsWebpackPlugin' } 208 | 209 | compiler.hooks.emit.tapAsync(plugin, emitPlugin) 210 | } else { 211 | compiler.plugin('after-emit', emitPlugin) 212 | } 213 | } 214 | } 215 | 216 | module.exports = AssetsWebpackPlugin 217 | -------------------------------------------------------------------------------- /lib/getAssetKind.js: -------------------------------------------------------------------------------- 1 | const camelcase = require('camelcase') 2 | 3 | const getFileExtension = require('./getFileExtension') 4 | 5 | module.exports = function getAssetKind (options, asset) { 6 | const ext = getFileExtension(asset) 7 | return camelcase(ext) 8 | } 9 | -------------------------------------------------------------------------------- /lib/getDynamicImportedChildAssets.js: -------------------------------------------------------------------------------- 1 | const getAssetsFromChildChunk = (options, chunk, loadingBehavior) => { 2 | const assets = [] 3 | 4 | if (chunk.assets) { 5 | chunk.assets.forEach(asset => { 6 | asset.loadingBehavior = loadingBehavior 7 | assets.push(asset) 8 | }) 9 | } 10 | 11 | if (options.includeAuxiliaryAssets && chunk.auxiliaryAssets) { 12 | chunk.auxiliaryAssets.forEach(asset => { 13 | asset.loadingBehavior = loadingBehavior 14 | assets.push(asset) 15 | }) 16 | } 17 | 18 | return assets 19 | } 20 | 21 | module.exports = function getDynamicImportedChildAssets (options, children) { 22 | const loadingBehaviors = ['preload', 'prefetch'] 23 | let assets = [] 24 | 25 | loadingBehaviors.forEach(loadingBehavior => { 26 | if (children[loadingBehavior]) { 27 | children[loadingBehavior].forEach(childChunk => { 28 | assets = [...assets, ...getAssetsFromChildChunk(options, childChunk, loadingBehavior)] 29 | }) 30 | } 31 | }) 32 | 33 | return assets 34 | } 35 | -------------------------------------------------------------------------------- /lib/getFileExtension.js: -------------------------------------------------------------------------------- 1 | module.exports = function getFileExtension (asset) { 2 | const extRegex = /\.([0-9a-z]+)(?=[?#])|(\.)(?:[\w]+)$/i 3 | const ext = asset.match(extRegex) 4 | 5 | return ext ? ext[0].slice(1) : '' 6 | } 7 | -------------------------------------------------------------------------------- /lib/isHMRUpdate.js: -------------------------------------------------------------------------------- 1 | const pathTemplate = require('./pathTemplate') 2 | 3 | module.exports = function isHMRUpdate (options, asset) { 4 | if (asset.includes('.hot-update.')) return true 5 | 6 | const hotUpdateChunkFilename = options.output.hotUpdateChunkFilename 7 | const hotUpdateTemplate = pathTemplate(hotUpdateChunkFilename) 8 | return hotUpdateTemplate.matches(asset) 9 | } 10 | -------------------------------------------------------------------------------- /lib/isSourceMap.js: -------------------------------------------------------------------------------- 1 | const pathTemplate = require('./pathTemplate') 2 | 3 | module.exports = function isSourceMap (options, asset) { 4 | const sourceMapFilename = options.output.sourceMapFilename 5 | const sourcemapTemplate = pathTemplate(sourceMapFilename) 6 | return sourcemapTemplate.matches(asset) 7 | } 8 | -------------------------------------------------------------------------------- /lib/output/createOutputWriter.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const fs = require('fs') 3 | const _ = require('lodash') 4 | 5 | const error = require('../utils/error') 6 | 7 | function sortAssets (assets) { 8 | return Object.keys(assets).map(i => ({ [i]: assets[i] })).sort((a, b) => { 9 | if (a.manifest) { 10 | return -1 11 | } 12 | 13 | if (b.manifest) { 14 | return 1 15 | } 16 | 17 | return 0 18 | }) 19 | } 20 | 21 | function orderAssets (assets, options) { 22 | return options.manifestFirst 23 | ? Object.assign({}, ...sortAssets(assets)) 24 | : assets 25 | } 26 | 27 | module.exports = function (options) { 28 | const update = options.update 29 | let firstRun = true 30 | 31 | options.processOutput = options.processOutput || function (assets) { 32 | return JSON.stringify(assets, null, options.prettyPrint ? 2 : null) 33 | } 34 | 35 | return function writeOutput (fileStream, newAssets, next) { 36 | // if options.update is false and we're on the first pass of a (possibly) multicompiler 37 | const overwrite = !update && firstRun 38 | const localFs = options.keepInMemory ? fileStream : fs 39 | 40 | function mkdirCallback (err) { 41 | if (err) handleMkdirError(err) 42 | 43 | const outputPath = options.keepInMemory 44 | ? localFs.join(options.path, options.filename) 45 | : path.join(options.path, options.filename) 46 | 47 | localFs.readFile(outputPath, 'utf8', function (err, data) { 48 | // if file does not exist, just write data to it 49 | if (err && err.code !== 'ENOENT') { 50 | return next(error('Could not read output file ' + outputPath, err)) 51 | } 52 | 53 | // if options.update is false and we're on first run, so start with empty data 54 | data = overwrite ? '{}' : data || '{}' 55 | 56 | let oldAssets 57 | try { 58 | oldAssets = JSON.parse(data) 59 | } catch (err) { 60 | oldAssets = {} 61 | } 62 | 63 | const assets = orderAssets(_.merge({}, oldAssets, newAssets), options) 64 | const output = options.processOutput(assets) 65 | if (output !== data) { 66 | localFs.writeFile(outputPath, output, function (err) { 67 | if (err) { 68 | return next(error('Unable to write to ' + outputPath, err)) 69 | } 70 | firstRun = false 71 | next() 72 | }) 73 | } else { 74 | next() 75 | } 76 | }) 77 | } 78 | 79 | function handleMkdirError (err) { 80 | return next(error('Could not create output folder ' + options.path, err)) 81 | } 82 | 83 | if (options.keepInMemory) { 84 | localFs.mkdirp(options.path, mkdirCallback) 85 | } else { 86 | fs.mkdir(options.path, { recursive: true }, mkdirCallback) 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/output/createQueuedWriter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Takes in a processor function, and returns a writer function. 3 | * 4 | * @param {Function} processor 5 | * 6 | * @return {Function} queuedWriter 7 | */ 8 | module.exports = function createQueuedWriter (processor) { 9 | const queue = [] 10 | 11 | const iterator = function (callback) { 12 | return function (err) { 13 | queue.shift() 14 | callback(err) 15 | 16 | const next = queue[0] 17 | if (next) { 18 | processor(next.fs, next.data, iterator(next.callback)) 19 | } 20 | } 21 | } 22 | 23 | return function queuedWriter (fs, data, callback) { 24 | const empty = !queue.length 25 | queue.push({ fs: fs, data: data, callback: callback }) 26 | 27 | if (empty) { 28 | // start processing 29 | processor(fs, data, iterator(callback)) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/pathTemplate.js: -------------------------------------------------------------------------------- 1 | const escapeRegExp = require('escape-string-regexp') 2 | 3 | const SIMPLE_PLACEHOLDER_RX = /^\[(id|name|file|query|filebase)]/i 4 | const HASH_PLACEHOLDER_RX = /^\[((?:full)?(?:chunk)?hash)(?::(\d+))?]/i 5 | 6 | const templateCache = Object.create(null) 7 | 8 | module.exports = function createTemplate (str) { 9 | if (!templateCache[str]) { 10 | templateCache[str] = new PathTemplate(str) 11 | } 12 | 13 | return templateCache[str] 14 | } 15 | 16 | function PathTemplate (template) { 17 | this.template = template 18 | this.fields = parseTemplate(template) 19 | this.matcher = createTemplateMatcher(this.fields) 20 | } 21 | 22 | PathTemplate.prototype = { 23 | 24 | constructor: PathTemplate, 25 | 26 | /** 27 | * Returns whether the given path matches this template. 28 | * 29 | * @param String data 30 | */ 31 | matches: function (path) { 32 | return this.matcher.test(path) 33 | }, 34 | 35 | /** 36 | * Applies data to this template and outputs a filename. 37 | * 38 | * @param Object data 39 | */ 40 | resolve: function (data) { 41 | return this.fields.reduce(function (output, field) { 42 | let replacement = '' 43 | const placeholder = field.placeholder 44 | const width = field.width 45 | 46 | if (field.prefix) { 47 | output += field.prefix 48 | } 49 | if (placeholder) { 50 | replacement = data[placeholder] || '' 51 | if (width && (placeholder === 'hash' || placeholder === 'fullhash' || placeholder === 'chunkhash')) { 52 | replacement = replacement.slice(0, width) 53 | } 54 | output += replacement 55 | } 56 | 57 | return output 58 | }, '') 59 | } 60 | } 61 | 62 | /** 63 | * Loop over the template string and return an array of objects in the form: 64 | * { 65 | * prefix: 'literal text', 66 | * placeholder: 'replacement field name' 67 | * [, width: maximum hash length for hash & chunkhash placeholders] 68 | * } 69 | * 70 | * The values in the object conceptually represent a span of literal text followed by a single replacement field. 71 | * If there is no literal text (which can happen if two replacement fields occur consecutively), 72 | * then prefix will be an empty string. 73 | * If there is no replacement field, then the value of placeholder will be null. 74 | * If the value of placeholder is either 'hash', 'fullhash', or 'chunkhash', then width will be a positive integer. 75 | * Otherwise it will be left undefined. 76 | */ 77 | function parseTemplate (str) { 78 | const fields = [] 79 | let char = '' 80 | let pos = 0 81 | let prefix = '' 82 | let match = null 83 | let input 84 | 85 | while (true) { // eslint-disable-line no-constant-condition 86 | char = str[pos] 87 | 88 | if (!char) { 89 | fields.push({ 90 | prefix: prefix, 91 | placeholder: null 92 | }) 93 | break 94 | } else if (char === '[') { 95 | input = str.slice(pos) 96 | match = SIMPLE_PLACEHOLDER_RX.exec(input) 97 | if (match) { 98 | fields.push({ 99 | prefix: prefix, 100 | placeholder: match[1].toLowerCase() 101 | }) 102 | pos += match[0].length 103 | prefix = '' 104 | continue 105 | } 106 | 107 | match = HASH_PLACEHOLDER_RX.exec(input) 108 | if (match) { 109 | fields.push({ 110 | prefix: prefix, 111 | placeholder: match[1].toLowerCase(), 112 | width: parseInt(match[2] || 0, 10) 113 | }) 114 | pos += match[0].length 115 | prefix = '' 116 | continue 117 | } 118 | } 119 | prefix += char 120 | pos++ 121 | } 122 | 123 | return fields 124 | } 125 | 126 | /** 127 | * Returns a RegExp which, given the replacement fields returned by parseTemplate(), 128 | * can match a file path against a path template. 129 | */ 130 | function createTemplateMatcher (fields) { 131 | const length = fields.length 132 | const pattern = fields.reduce(function (pattern, field, i) { 133 | if (i === 0) { 134 | pattern = '^' 135 | } 136 | if (field.prefix) { 137 | pattern += '(' + escapeRegExp(field.prefix) + ')' 138 | } 139 | if (field.placeholder) { 140 | switch (field.placeholder) { 141 | case 'hash': 142 | case 'fullhash': 143 | case 'chunkhash': 144 | pattern += '[0-9a-fA-F]' 145 | pattern += field.width ? '{1,' + field.width + '}' : '+' 146 | break 147 | case 'id': 148 | case 'name': 149 | case 'file': 150 | case 'filebase': 151 | pattern += '.+?' 152 | break 153 | case 'query': 154 | pattern += '(?:\\?.+?)?' 155 | break 156 | } 157 | } 158 | if (i === length - 1) { 159 | pattern += '$' 160 | } 161 | 162 | return pattern 163 | }, '') 164 | 165 | return new RegExp(pattern) 166 | } 167 | -------------------------------------------------------------------------------- /lib/utils/error.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | 3 | module.exports = function pluginError (message, previousError) { 4 | const err = new Error('[AssetsWebpackPlugin] ' + message) 5 | 6 | return previousError ? _.assign(err, previousError) : err 7 | } 8 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Zach Toben 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "assets-webpack-plugin", 3 | "version": "7.1.1", 4 | "description": "Emits a json file with assets paths", 5 | "main": "dist/index.js", 6 | "engines": { 7 | "node": ">=10.x.x" 8 | }, 9 | "scripts": { 10 | "lint": "standard --verbose | snazzy", 11 | "fix-lint": "standard --fix", 12 | "test": "mocha test", 13 | "build": "babel index.js --out-dir dist && babel lib --out-dir dist/lib", 14 | "prepublish": "npm run build" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/ztoben/assets-webpack-plugin.git" 19 | }, 20 | "keywords": [ 21 | "webpack", 22 | "plugin", 23 | "generate", 24 | "assets", 25 | "hashes" 26 | ], 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/ztoben/assets-webpack-plugin/issues" 30 | }, 31 | "homepage": "https://github.com/ztoben/assets-webpack-plugin", 32 | "files": [ 33 | "index.js", 34 | "dist/" 35 | ], 36 | "devDependencies": { 37 | "babel-cli": "6.26.0", 38 | "babel-core": "6.26.3", 39 | "babel-preset-env": "1.7.0", 40 | "chai": "4.3.4", 41 | "css-loader": "5.2.7", 42 | "mini-css-extract-plugin": "1.6.2", 43 | "mocha": "8.4.0", 44 | "rimraf": "3.0.2", 45 | "snazzy": "9.0.0", 46 | "standard": "16.0.3", 47 | "style-loader": "2.0.0", 48 | "webpack": "5.53.0", 49 | "webpack-subresource-integrity": "1.5.2" 50 | }, 51 | "dependencies": { 52 | "camelcase": "^6.0.0", 53 | "escape-string-regexp": "4.0.0", 54 | "lodash": "^4.17.21" 55 | }, 56 | "peerDependencies": { 57 | "webpack": ">=5.0.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | __IMPORTANT: Please do not create a Pull Request without creating an issue first.__ 2 | 3 | _Any change needs to be discussed before proceeding. Failure to do so may result in the rejection of the pull request._ 4 | 5 | Please provide enough information so that others can review your pull request: 6 | 7 | 8 | 9 | Explain the __details__ for making this change. What existing problem does the pull request solve? 10 | 11 | 12 | 13 | __Test plan (required)__ 14 | 15 | Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI. 16 | 17 | 18 | 19 | __Code formatting__ 20 | 21 | 22 | 23 | __Closing issues__ 24 | 25 | Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if such). 26 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # assets-webpack-plugin 2 | 3 | [](https://npmjs.org/package/assets-webpack-plugin) 4 | [](https://npmjs.org/package/assets-webpack-plugin) 5 | [](https://opensource.org/licenses/MIT) 6 | [](https://travis-ci.org/ztoben/assets-webpack-plugin) 7 | [](https://ci.appveyor.com/project/ztoben/assets-webpack-plugin) 8 | 9 | Webpack plugin that emits a json file with assets paths. 10 | 11 | ## Table of Contents 12 | 13 | * [Install](#install) 14 | * [Why Is This Useful?](#why-is-this-useful) 15 | * [Example output:](#example-output) 16 | * [Configuration](#configuration) 17 | * [Options](#options) 18 | * [`filename`](#filename) 19 | * [`fullPath`](#fullpath) 20 | * [`removeFullPathAutoPrefix`](#removefullpathautoprefix) 21 | * [`includeManifest`](#includemanifest) 22 | * [`manifestFirst`](#manifestfirst) 23 | * [`path`](#path) 24 | * [`useCompilerPath`](#usecompilerpath) 25 | * [`prettyPrint`](#prettyprint) 26 | * [`processOutput`](#processoutput) 27 | * [`update`](#update) 28 | * [`metadata`](#metadata) 29 | * [`includeAllFileTypes`](#includeallfileTtypes) 30 | * [`fileTypes`](#filetypes) 31 | * [`keepInMemory`](#keepinmemory) 32 | * [`integrity`](#integrity) 33 | * [`entrypoints`](#entrypoints) 34 | * [`includeFilesWithoutChunk`](#includefileswithoutchunk) 35 | * [Using in multi-compiler mode](#using-in-multi-compiler-mode) 36 | * [Using this with Rails](#using-this-with-rails) 37 | * [Using this with ASP.NET Core](#using-this-with-aspnet-core) 38 | * [Test](#test) 39 | 40 | ## Install 41 | 42 | ⚠️ Starting with version 6, this plugin works with Webpack 5+. 43 | 44 | If you are working with an older version of Webpack, you can use the most recent 5.x.x release (`5.1.2`). 45 | 46 | ```sh 47 | npm install assets-webpack-plugin --save-dev 48 | ``` 49 | 50 | If you're using Webpack 4 or below: 51 | 52 | ```sh 53 | npm install assets-webpack-plugin@5.1.2 --save-dev 54 | ``` 55 | 56 | ## Why Is This Useful? 57 | 58 | When working with Webpack you might want to generate your bundles with a generated hash in them (for cache busting). 59 | 60 | This plug-in outputs a json file with the paths of the generated assets so you can find them from somewhere else. 61 | 62 | ### Example output: 63 | 64 | The output is a JSON object in the form: 65 | 66 | ```json 67 | { 68 | "bundle_name": { 69 | "asset_kind": "/public/path/to/asset" 70 | } 71 | } 72 | ``` 73 | 74 | Where: 75 | 76 | * `"bundle_name"` is the name of the bundle (the key of the entry object in your webpack config, or "main" if your entry is an array). 77 | * `"asset_kind"` is the camel-cased file extension of the asset 78 | 79 | For example, given the following webpack config: 80 | 81 | ```js 82 | { 83 | entry: { 84 | one: ['src/one.js'], 85 | two: ['src/two.js'] 86 | }, 87 | output: { 88 | path: path.join(__dirname, "public", "js"), 89 | publicPath: "/js/", 90 | filename: '[name]_[hash].bundle.js' 91 | } 92 | } 93 | ``` 94 | 95 | The plugin will output the following json file: 96 | 97 | ```json 98 | { 99 | "one": { 100 | "js": "/js/one_2bb80372ebe8047a68d4.bundle.js" 101 | }, 102 | "two": { 103 | "js": "/js/two_2bb80372ebe8047a68d4.bundle.js" 104 | } 105 | } 106 | ``` 107 | 108 | ## Configuration 109 | 110 | In your webpack config include the plug-in. And add it to your config: 111 | 112 | ```js 113 | var path = require('path') 114 | var AssetsPlugin = require('assets-webpack-plugin') 115 | var assetsPluginInstance = new AssetsPlugin() 116 | 117 | module.exports = { 118 | // ... 119 | output: { 120 | path: path.join(__dirname, "public", "js"), 121 | filename: "[name]-bundle-[hash].js", 122 | publicPath: "/js/" 123 | }, 124 | // .... 125 | plugins: [assetsPluginInstance] 126 | } 127 | ``` 128 | 129 | ### Options 130 | 131 | You can pass the following options: 132 | 133 | #### `filename` 134 | 135 | Optional. `webpack-assets.json` by default. 136 | 137 | Name for the created json file. 138 | 139 | ```js 140 | new AssetsPlugin({filename: 'assets.json'}) 141 | ``` 142 | 143 | #### `fullPath` 144 | 145 | Optional. `true` by default. 146 | 147 | If `false` the output will not include the full path 148 | of the generated file. 149 | 150 | ```js 151 | new AssetsPlugin({fullPath: false}) 152 | ``` 153 | 154 | e.g. 155 | 156 | `/public/path/bundle.js` vs `bundle.js` 157 | 158 | #### `removeFullPathAutoPrefix` 159 | 160 | Optional. `false` by default. 161 | 162 | If `true` the full path will automatically be stripped of the `/auto/` prefix generated by webpack. 163 | 164 | ```js 165 | new AssetsPlugin({removeFullPathAutoPrefix: true}) 166 | ``` 167 | 168 | e.g. 169 | 170 | `/public/path/bundle.js` vs `bundle.js` 171 | 172 | #### `includeManifest` 173 | 174 | Optional. `false` by default. 175 | 176 | Inserts the manifest javascript as a `text` property in your assets. 177 | Accepts the name or names of your manifest chunk. A manifest is the last CommonChunk that 178 | only contains the webpack bootstrap code. This is useful for production 179 | use when you want to inline the manifest in your HTML skeleton for long-term caching. 180 | See [issue #1315](https://github.com/webpack/webpack/issues/1315) 181 | or [a blog post](https://medium.com/@matt.krick/a-production-ready-realtime-saas-with-webpack-7b11ba2fa5b0#.p1vvfr3bm) 182 | to learn more. 183 | 184 | ```js 185 | new AssetsPlugin({includeManifest: 'manifest'}) 186 | // assets.json: 187 | // {entries: {manifest: {js: `hashed_manifest.js`, text: 'function(modules)...'}}} 188 | // 189 | // Your html template: 190 | // 193 | ``` 194 | 195 | The `includeManifest` option also accepts an array of manifests: 196 | 197 | ```js 198 | new AssetsPlugin({includeManifest: ['manifest1', 'manifest2']}) 199 | // assets.json: 200 | // {entries: { 201 | // manifest1: {js: `hashed_manifest.js`, text: 'function(modules)...'}, 202 | // manifest2: {js: `hashed_manifest.js`, text: 'function(modules)...'} 203 | // }} 204 | ``` 205 | 206 | #### `manifestFirst` 207 | 208 | Optional. `false` by default. 209 | 210 | Orders the assets output so that manifest is the first entry. This is useful for cases where script tags are generated 211 | from the assets json output, and order of import is important. 212 | 213 | ```js 214 | new AssetsPlugin({manifestFirst: true}) 215 | ``` 216 | 217 | #### `path` 218 | 219 | Optional. Defaults to the current directory. 220 | 221 | Path where to save the created JSON file. Will default to the highest level of the project unless useCompilerPath is specified. 222 | 223 | ```js 224 | new AssetsPlugin({path: path.join(__dirname, 'app', 'views')}) 225 | ``` 226 | 227 | #### `useCompilerPath` 228 | 229 | ```js 230 | new AssetsPlugin({useCompilerPath: true}) 231 | ``` 232 | 233 | Will override the path to use the compiler output path set in your webpack config. 234 | 235 | #### `prettyPrint` 236 | 237 | Optional. `false` by default. 238 | 239 | Whether to format the JSON output for readability. 240 | 241 | ```js 242 | new AssetsPlugin({prettyPrint: true}) 243 | ``` 244 | 245 | #### `processOutput` 246 | 247 | Optional. Defaults is JSON stringify function. 248 | 249 | Formats the assets output. 250 | 251 | ```js 252 | new AssetsPlugin({ 253 | processOutput: function (assets) { 254 | return 'window.staticMap = ' + JSON.stringify(assets) 255 | } 256 | }) 257 | ``` 258 | 259 | #### `update` 260 | 261 | Optional. `false` by default. 262 | 263 | When set to `true`, the output JSON file will be updated instead of overwritten. 264 | 265 | ```js 266 | new AssetsPlugin({update: true}) 267 | ``` 268 | 269 | #### `metadata` 270 | 271 | Inject metadata into the output file. All values will be injected into the key "metadata". 272 | 273 | ```js 274 | new AssetsPlugin({metadata: {version: 123}}) 275 | // Manifest will now contain: 276 | // { 277 | // metadata: {version: 123} 278 | // } 279 | ``` 280 | 281 | #### `includeAllFileTypes` 282 | 283 | Optional. `true` by default. 284 | 285 | When set false, falls back to the `fileTypes` option array to decide which file types to include in the assets file. 286 | 287 | ```js 288 | new AssetsPlugin({includeAllFileTypes: false}) 289 | ``` 290 | 291 | #### `fileTypes` 292 | 293 | Optional. `['js', 'css']` by default. 294 | 295 | When set and `includeAllFileTypes` is set false, only assets matching these types will be included in the assets file. 296 | 297 | ```js 298 | new AssetsPlugin({fileTypes: ['js', 'jpg']}) 299 | ``` 300 | 301 | #### `keepInMemory` 302 | 303 | Optional. `false` by default. 304 | 305 | When set the assets file will only be generated in memory while running `webpack-dev-server` and not written to disk. 306 | 307 | ```js 308 | new AssetsPlugin({keepInMemory: true}) 309 | ``` 310 | 311 | #### `integrity` 312 | 313 | Optional. `false` by default. 314 | 315 | When set the output from [webpack-subresource-integrity](https://github.com/waysact/webpack-subresource-integrity) is included in the assets file. 316 | 317 | Please make sure you have `webpack-subresource-integrity` installed and included in your webpack plugins. 318 | 319 | ```js 320 | new AssetsPlugin({integrity: true}) 321 | ``` 322 | 323 | Output will now look like this: 324 | 325 | ```json 326 | { 327 | "main": { 328 | "js": "/bundle.js", 329 | "jsIntegrity": "sha256-ANGwtktWN96nvBI/cjekdTvd0Dwf7SciIFTQ2lpTxGc= sha384-Ly439pF3K+J8hnhk1BEcjKnv1R9BApFYVIVJvr64PcgBjdT4N7hfPzQynItHwcaO" 330 | }, 331 | "vendors~main": { 332 | "js": "/1.bundle.js", 333 | "jsIntegrity": "sha256-yqNi1hgeAdkXVOORgmVMeX+cbuXikoj6I8qWZjPegsA= sha384-4X75tnsGDwnwL5kBUPsx2ko9DeWy0xM8BcDQdoR185yho+OnxjjPXl2wCdebLWTG" 334 | } 335 | } 336 | ``` 337 | 338 | #### `entrypoints` 339 | 340 | Optional. `false` by default. 341 | 342 | If the 'entrypoints' option is given, the output will be limited to the entrypoints and the chunks associated with them. 343 | 344 | ```js 345 | new AssetsPlugin({entrypoints: true}) 346 | ``` 347 | 348 | #### `includeFilesWithoutChunk` 349 | 350 | Optional. `false` by default. 351 | 352 | When set and `entrypoints` is set true, will output any files that are part of the unnamed chunk to an additional unnamed ("") entry. 353 | 354 | ```js 355 | new AssetsPlugin({includeFilesWithoutChunk: true}) 356 | ``` 357 | 358 | #### `includeAuxiliaryAssets` 359 | 360 | Optional. `false` by default. 361 | 362 | When set, will output any files that are part of the chunk and marked as auxiliary assets. 363 | 364 | ```js 365 | new AssetsPlugin({includeAuxiliaryAssets: true}) 366 | ``` 367 | 368 | #### `includeDynamicImportedAssets` 369 | 370 | Optional. `false` by default. 371 | 372 | When set, will output any files that are part of the chunk and marked as preloadable or prefechtable child assets via a dynamic import. 373 | See: https://webpack.js.org/guides/code-splitting/#prefetchingpreloading-modules 374 | 375 | ```js 376 | new AssetsPlugin({includeDynamicImportedAssets: true}) 377 | ``` 378 | 379 | ### Using in multi-compiler mode 380 | 381 | If you use webpack multi-compiler mode and want your assets written to a single file, 382 | you __must__ use the same instance of the plugin in the different configurations. 383 | 384 | For example: 385 | 386 | ```js 387 | var webpack = require('webpack') 388 | var AssetsPlugin = require('assets-webpack-plugin') 389 | var assetsPluginInstance = new AssetsPlugin() 390 | 391 | webpack([ 392 | { 393 | entry: {one: 'src/one.js'}, 394 | output: {path: 'build', filename: 'one-bundle.js'}, 395 | plugins: [assetsPluginInstance] 396 | }, 397 | { 398 | entry: {two:'src/two.js'}, 399 | output: {path: 'build', filename: 'two-bundle.js'}, 400 | plugins: [assetsPluginInstance] 401 | } 402 | ]) 403 | ``` 404 | 405 | ### Using this with Rails 406 | 407 | You can use this with Rails to find the bundled Webpack assets via Sprockets. 408 | In `ApplicationController` you might have: 409 | 410 | ```ruby 411 | def script_for(bundle) 412 | path = Rails.root.join('app', 'views', 'webpack-assets.json') # This is the file generated by the plug-in 413 | file = File.read(path) 414 | json = JSON.parse(file) 415 | json[bundle]['js'] 416 | end 417 | ``` 418 | 419 | Then in the actions: 420 | 421 | ```ruby 422 | def show 423 | @script = script_for('clients') # this will retrieve the bundle named 'clients' 424 | end 425 | ``` 426 | 427 | And finally in the views: 428 | 429 | ```erb 430 |