Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webpack5 JSON module import with Object destructuring assignment should drop unused JSON properties #11676

Closed
dr-js opened this issue Oct 14, 2020 · 14 comments

Comments

@dr-js
Copy link

dr-js commented Oct 14, 2020

Feature request

Webpack5 will emit a warning when a non-default export is used, but using the default JSON export may cause the whole file being packed, depending on the type of assignment being used.

The whole test setup: test-webpack5-json-module-20201017.zip (with webpack@5.1.3)

The test can be run with npm i && npm t after unzip.

[Sample log of one test run]
~/TEST/test-webpack5-json-module# npm t

> test-webpack5-json-module@0.0.0 test /root/TEST/test-webpack5-json-module
> npm run build-test-0 && npm run build-test-1 && npm run build-test-2 && npm run build-test-3 && npm run build-test-4


> test-webpack5-json-module@0.0.0 build-test-0 /root/TEST/test-webpack5-json-module
> node build.js test-0-non-default-import.js

index: /root/TEST/test-webpack5-json-module/test-0-non-default-import.js
path: /root/TEST/test-webpack5-json-module
filename: output-test-0-non-default-import.js
[compile] start
{
  moduleIdentifier: '/root/TEST/test-webpack5-json-module/test-0-non-default-import.js',
  moduleName: './test-0-non-default-import.js',
  loc: '2:14-25',
  message: "Should not import the named export 'name' (imported as 'packageName') from default-exporting module (only default export is available soon)",
  details: '    at HarmonyImportSpecifierDependency.getLinkingErrors (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportDependency.js:136:7)\n' +
    '    at HarmonyImportSpecifierDependency._getErrors (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:177:15)\n' +
    '    at HarmonyImportSpecifierDependency.getWarnings (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:153:15)\n' +
    '    at Compilation.reportDependencyErrorsAndWarnings (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/Compilation.js:2063:24)\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/webpack/lib/Compilation.js:1702:10\n' +
    '    at _next2 (eval at create (/root/TEST/test-webpack5-json-module/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:25:1)\n' +
    '    at eval (eval at create (/root/TEST/test-webpack5-json-module/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:52:1)\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/webpack/lib/FlagDependencyExportsPlugin.js:332:11\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/neo-async/async.js:2830:7\n' +
    '    at Object.each (/root/TEST/test-webpack5-json-module/node_modules/neo-async/async.js:2850:39)'
}
{
  moduleIdentifier: '/root/TEST/test-webpack5-json-module/test-0-non-default-import.js',
  moduleName: './test-0-non-default-import.js',
  loc: '2:27-41',
  message: "Should not import the named export 'version' (imported as 'packageVersion') from default-exporting module (only default export is available soon)",
  details: '    at HarmonyImportSpecifierDependency.getLinkingErrors (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportDependency.js:136:7)\n' +
    '    at HarmonyImportSpecifierDependency._getErrors (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:177:15)\n' +
    '    at HarmonyImportSpecifierDependency.getWarnings (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:153:15)\n' +
    '    at Compilation.reportDependencyErrorsAndWarnings (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/Compilation.js:2063:24)\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/webpack/lib/Compilation.js:1702:10\n' +
    '    at _next2 (eval at create (/root/TEST/test-webpack5-json-module/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:25:1)\n' +
    '    at eval (eval at create (/root/TEST/test-webpack5-json-module/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:52:1)\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/webpack/lib/FlagDependencyExportsPlugin.js:332:11\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/neo-async/async.js:2830:7\n' +
    '    at Object.each (/root/TEST/test-webpack5-json-module/node_modules/neo-async/async.js:2850:39)'
}
[compile] done
[FILE_INPUT] test-0-non-default-import.js ======================================
import { name as packageName, version as packageVersion } from './package.json'
console.log({ packageName, packageVersion })
[FILE_OUTPUT] output-test-0-non-default-import.js ==============================
(()=>{"use strict";const e=JSON.parse('{"u2":"test-webpack5-json-module","i8":"0.0.0"}');console.log({packageName:e.u2,packageVersion:e.i8})})();
================================================================================
[main] done

> test-webpack5-json-module@0.0.0 build-test-1 /root/TEST/test-webpack5-json-module
> node build.js test-1-default-import-basic-assignment.js

index: /root/TEST/test-webpack5-json-module/test-1-default-import-basic-assignment.js
path: /root/TEST/test-webpack5-json-module
filename: output-test-1-default-import-basic-assignment.js
[compile] start
[compile] done
[FILE_INPUT] test-1-default-import-basic-assignment.js =========================
import PACKAGE_JSON from './package.json'
const packageName = PACKAGE_JSON.name
const packageVersion = PACKAGE_JSON.version
console.log({ packageName, packageVersion })
[FILE_OUTPUT] output-test-1-default-import-basic-assignment.js =================
(()=>{"use strict";const e=JSON.parse('{"u2":"test-webpack5-json-module","i8":"0.0.0"}'),s=e.u2,a=e.i8;console.log({packageName:s,packageVersion:a})})();
================================================================================
[main] done

> test-webpack5-json-module@0.0.0 build-test-2 /root/TEST/test-webpack5-json-module
> node build.js test-2-default-import-object-destructuring-assignment.js

index: /root/TEST/test-webpack5-json-module/test-2-default-import-object-destructuring-assignment.js
path: /root/TEST/test-webpack5-json-module
filename: output-test-2-default-import-object-destructuring-assignment.js
[compile] start
[compile] done
[FILE_INPUT] test-2-default-import-object-destructuring-assignment.js ==========
import PACKAGE_JSON from './package.json'
const { name: packageName, version: packageVersion } = PACKAGE_JSON
console.log({ packageName, packageVersion })
[FILE_OUTPUT] output-test-2-default-import-object-destructuring-assignment.js ==
(()=>{"use strict";const e=JSON.parse('{"private":true,"name":"test-webpack5-json-module","version":"0.0.0","scripts":{"test":"npm run build-test-0 && npm run build-test-1 && npm run build-test-2 && npm run build-test-3 && npm run build-test-4","build-test-0":"node build.js test-0-non-default-import.js","build-test-1":"node build.js test-1-default-import-basic-assignment.js","build-test-2":"node build.js test-2-default-import-object-destructuring-assignment.js","build-test-3":"node build.js test-3-require-basic-assignment.js","build-test-4":"node build.js test-4-require-object-destructuring-assignment.js"},"devDependencies":{"webpack":"5.1.3"}}'),{name:t,version:s}=e;console.log({packageName:t,packageVersion:s})})();
================================================================================
[main] done

> test-webpack5-json-module@0.0.0 build-test-3 /root/TEST/test-webpack5-json-module
> node build.js test-3-require-basic-assignment.js

index: /root/TEST/test-webpack5-json-module/test-3-require-basic-assignment.js
path: /root/TEST/test-webpack5-json-module
filename: output-test-3-require-basic-assignment.js
[compile] start
[compile] done
[FILE_INPUT] test-3-require-basic-assignment.js ================================
const PACKAGE_JSON = require('./package.json')
const packageName = PACKAGE_JSON.name
const packageVersion = PACKAGE_JSON.version
console.log({ packageName, packageVersion })
[FILE_OUTPUT] output-test-3-require-basic-assignment.js ========================
(()=>{var e={306:e=>{"use strict";e.exports=JSON.parse('{"private":true,"name":"test-webpack5-json-module","version":"0.0.0","scripts":{"test":"npm run build-test-0 && npm run build-test-1 && npm run build-test-2 && npm run build-test-3 && npm run build-test-4","build-test-0":"node build.js test-0-non-default-import.js","build-test-1":"node build.js test-1-default-import-basic-assignment.js","build-test-2":"node build.js test-2-default-import-object-destructuring-assignment.js","build-test-3":"node build.js test-3-require-basic-assignment.js","build-test-4":"node build.js test-4-require-object-destructuring-assignment.js"},"devDependencies":{"webpack":"5.1.3"}}')}},t={};function s(n){if(t[n])return t[n].exports;var i=t[n]={exports:{}};return e[n](i,i.exports,s),i.exports}(()=>{const e=s(306),t=e.name,n=e.version;console.log({packageName:t,packageVersion:n})})()})();
================================================================================
[main] done

> test-webpack5-json-module@0.0.0 build-test-4 /root/TEST/test-webpack5-json-module
> node build.js test-4-require-object-destructuring-assignment.js

index: /root/TEST/test-webpack5-json-module/test-4-require-object-destructuring-assignment.js
path: /root/TEST/test-webpack5-json-module
filename: output-test-4-require-object-destructuring-assignment.js
[compile] start
[compile] done
[FILE_INPUT] test-4-require-object-destructuring-assignment.js =================
const { name: packagename, version: packageVersion } = require('./package.json')
console.log({ packageName, packageVersion })
[FILE_OUTPUT] output-test-4-require-object-destructuring-assignment.js =========
(()=>{var e={306:e=>{"use strict";e.exports=JSON.parse('{"private":true,"name":"test-webpack5-json-module","version":"0.0.0","scripts":{"test":"npm run build-test-0 && npm run build-test-1 && npm run build-test-2 && npm run build-test-3 && npm run build-test-4","build-test-0":"node build.js test-0-non-default-import.js","build-test-1":"node build.js test-1-default-import-basic-assignment.js","build-test-2":"node build.js test-2-default-import-object-destructuring-assignment.js","build-test-3":"node build.js test-3-require-basic-assignment.js","build-test-4":"node build.js test-4-require-object-destructuring-assignment.js"},"devDependencies":{"webpack":"5.1.3"}}')}},t={};function s(n){if(t[n])return t[n].exports;var i=t[n]={exports:{}};return e[n](i,i.exports,s),i.exports}(()=>{const{name:e,version:t}=s(306);console.log({packageName,packageVersion:t})})()})();
================================================================================
[main] done
// FILE: `test-0-non-default-import.js`
// BUILD: emits warning `Should not import the named export 'name' (imported as 'packageName'...`
// OUTPUT: no unused JSON properties: `const r=JSON.parse('{"u2":"test-webpack5-json-module","i8":"0.0.0"}')`
import { name as packageName, version as packageVersion } from './package.json'
console.log({ packageName, packageVersion })

// FILE: `test-1-default-import-basic-assignment.js`
// BUILD: no warning
// OUTPUT: no unused JSON properties: `const r=JSON.parse('{"u2":"test-webpack5-json-module","i8":"0.0.0"}')`
import PACKAGE_JSON from './package.json'
const packageName = PACKAGE_JSON.name
const packageVersion = PACKAGE_JSON.version
console.log({ packageName, packageVersion })

// FILE: `test-2-default-import-object-destructuring-assignment.js`
// BUILD: no warning
// OUTPUT: full JSON properties: `const n=JSON.parse('{"private":true,"name":"test-webpack5-json-module","version":"0.0.0","scripts":{"test":"...`
import PACKAGE_JSON from './package.json'
const { name: packageName, version: packageVersion } = PACKAGE_JSON
console.log({ packageName, packageVersion })

// FILE: `test-3-require-basic-assignment.js`
// BUILD: no warning
// OUTPUT: full JSON properties: `e.exports=JSON.parse('{"private":true,"name":"test-webpack5-json-module","version":"0.0.0","scripts":{"test":"...`
const PACKAGE_JSON = require('./package.json')
const packageName = PACKAGE_JSON.name
const packageVersion = PACKAGE_JSON.version
console.log({ packageName, packageVersion })

// FILE: `test-4-require-object-destructuring-assignment.js`
// BUILD: no warning
// OUTPUT: full JSON properties: `e.exports=JSON.parse('{"private":true,"name":"test-webpack5-json-module","version":"0.0.0","scripts":{"test":"`
const { name: packagename, version: packageVersion } = require('./package.json')
console.log({ packageName, packageVersion })

// FILE: `package.json`
{
  "private": true,
  "name": "test-webpack5-json-module",
  "version": "0.0.0",
  "scripts": { "test": "... omited ..." },
  "devDependencies": { "webpack": "5.1.0" }
}

// webpack config from `build.js`
  const config = {
    mode: 'production',
    target: 'node14',
    output: { path, filename },
    entry: { index }
  }

What is the expected behavior?

When using Object destructuring assignment and the JSON module the output can drop unused JSON properties, like with normal assignment.

What is motivation or use case for adding/changing the behavior?

To provide more coding style choice, and avoid syntax trap causing output bloat and potential info leak, since one common JSON import source is package.json, and people put all things inside.

How should this be implemented in your opinion?
Are you willing to work on this yourself?

Sorry, currently I have no idea how webpack internal works.

dr-js added a commit to dr-js/dr-run that referenced this issue Oct 14, 2020
notable change:
- break: use `@dr-js/*@0.4.0-dev*`
- break: change to new server|module pattern
- add: feature: Weblog with Markdown
- add: use `ActionJSON` to trigger generate Weblog
- add: ci: use GitHub Action
- todo: wait: terser/terser#851
- todo: check: webpack/webpack#11676
- script sort
- package update
@dnalborczyk
Copy link
Contributor

@dr-js the warning only appears for named json imports, not for anything you might or might not destructure afterwards. there are differences between es6 modules and commonjs.

// commonjs, destructuring
const { foo: foobar } = require('./bar')
// es6 module, named import
import { foo as foobar } from './bar'

@dnalborczyk
Copy link
Contributor

dnalborczyk commented Oct 17, 2020

additional information: #9246

to give you some background on the warning:

how do you import the following JSON with a named import? what should be imported in this case, the whole JSON structure or just the "default" field?

import { default as foo} from './foo.json'
// which is the equivalent for:
import foo from './foo.json'
{
  "default": "foo"
}

the other problem is that not everything is an "object". how do you import the following with named imports?

[0,1,2,3,4]

or null, a string, a number or a boolean value? all are valid JSON.

@dr-js
Copy link
Author

dr-js commented Oct 17, 2020

@dnalborczyk thanks for expanding the explanation, and I'm with you on locking JSON to default import only.

The feature request is about support drop unused JSON properties when the Object destructuring assignment is used on the imported JSON Object.

Your point on fallback to using the require() on JSON is also inspiring, I'll update the test to see if this works with JSON Object destructuring assignment, since the syntax is equally valid and compact, and browser JSON import is still not fully SPECed.

@dnalborczyk
Copy link
Contributor

dnalborczyk commented Oct 17, 2020

The feature request is about support drop unused JSON properties when the Object destructuring assignment is used on the imported JSON Object.

I'm not sure if that would be even feasible, maybe in only really simple cases?
what if someone takes the json structure and puts it in a map, or a database...? and all the sudden there's stuff missing.

or the structure might be re-exported: export { default } from './some.json'

Your point on fallback to using the require() on JSON is also inspiring,

sorry, that wasn't meant as a fallback suggestion. I don't know if webpack does any treeshaking in that case either. that was just to line out the difference between destructuring and named imports. there are more differences than just the syntax.

I think it's best explained here, which I think also applies as a case against tree shaking:
https://github.com/tc39/proposal-json-modules#why-dont-json-modules-support-named-exports
"It makes sense to think of a JSON document as conceptually "a single thing" rather than several things that happen to be side-by-side in a file."

@dr-js
Copy link
Author

dr-js commented Oct 17, 2020

I've added 2 more test, and the bad news is currently using require() will not drop unused JSON properties.

For the record, currently my only JSON import usage is for getting name and version from package.json, should drop all unused package.json data after webpack, and have a valid compact syntax for Node,js, babel and webpack.

@sokra
Copy link
Member

sokra commented Oct 17, 2020

// FILE: `test-1-default-import-basic-assignment.js`​// BUILD: no warning​// OUTPUT: no unused JSON properties: `const r=JSON.parse('{"u2":"test-webpack5-json-module","i8":"0.0.0"}')`​import​ ​PACKAGE_JSON​ ​from​ ​'./package.json'​
​const​ ​packageName​ ​=​ ​PACKAGE_JSON.name​
​const​ ​packageVersion​ ​=​ ​PACKAGE_JSON.version​
​console.log({​ packageName​,​ packageVersion ​})

This works.

Destructuring will eventually supported too, but we currently paused feature development

@dr-js
Copy link
Author

dr-js commented Oct 17, 2020

Yeah, that's the code style I'm using, since I prefer to limit the code dependency at top of the file.

It's good to know this feature is coming, and thank you all for the hard work!

@rjgotten
Copy link

rjgotten commented Oct 26, 2020

Hate to be that guy, but ...

Have you considered to just use a custom loader keyed on /([\\/])?package\.json$/i to explictly remove sensitive information?
That kind of scrubber seems faa------r more appropriate for these cases.

@dr-js
Copy link
Author

dr-js commented Oct 26, 2020

@rjgotten I think you're right, for my usage this is the exact need.
I'll later try write a JSON trim loader & test how it goes.

@dr-js
Copy link
Author

dr-js commented Oct 27, 2020

The loader webpack-json-pick-loader.js:

const EXPORT_MODE_EXPORT_EACH = 'export-each' // export like: `export const ${key} = ${JSON.stringify(value)}`, need set `type: 'javascript/auto'`, check: https://webpack.js.org/configuration/module/#ruletype
const EXPORT_MODE_EXPORT_DEFAULT = 'export-default' // export like: `export default { ${key0}, ${key1}, ${key2}, ... }`, need set `type: 'javascript/auto'`
const EXPORT_MODE_EXPORT_BOTH = 'export-both' // export both named and default, need set `type: 'javascript/auto'`
const EXPORT_MODE_JSON = 'json' // export as picked JSON Object String

const EXPORT_MODE_LIST = [ EXPORT_MODE_EXPORT_EACH, EXPORT_MODE_EXPORT_DEFAULT, EXPORT_MODE_EXPORT_BOTH, EXPORT_MODE_JSON ]

const JSONPickLoader = function (sourceString) {
  const sourceObject = JSON.parse(sourceString)
  if (!isBasicObject(sourceObject)) throw new Error(`[JSONPickLoader] source file should be Object JSON, got: ${String(sourceObject)}`)

  const { query: options } = this // https://webpack.js.org/api/loaders/#thisquery
  if (!isBasicObject(options)) throw new Error(`[JSONPickLoader] only JSON option supported, got: ${String(options)}`) // https://github.com/webpack/loader-utils/blob/v2.0.0/lib/getOptions.js#L12-L15

  const { keys = [], exportMode = EXPORT_MODE_EXPORT_BOTH } = options // NOTE: names in `options.keys` should be valid JS variable names.
  if (!EXPORT_MODE_LIST.includes(exportMode)) throw new Error(`[JSONPickLoader] invalid exportMode: ${String(exportMode)}`)

  const outputStringList = []
  switch (exportMode) {
    case EXPORT_MODE_EXPORT_EACH: {
      for (const key of keys) {
        const value = sourceObject[ key ]
        verifyPick(key, value)
        outputStringList.push(`export const ${key} = ${JSON.stringify(value)}`)
      }
      break
    }
    case EXPORT_MODE_EXPORT_DEFAULT:
    case EXPORT_MODE_EXPORT_BOTH: {
      const isBothMode = exportMode === EXPORT_MODE_EXPORT_BOTH
      const exportItemList = []
      for (let index = 0, indexMax = keys.length; index < indexMax; index++) {
        const key = keys[ index ]
        const value = sourceObject[ key ]
        verifyPick(key, value)
        outputStringList.push(isBothMode ? `export const ${key} = ${JSON.stringify(value)}` : `const _${index} = ${JSON.stringify(value)}`)
        exportItemList.push(isBothMode ? key : `_${index} as ${key}`)
      }
      outputStringList.push('export default {', exportItemList.join(','), '}')
      break
    }
    case EXPORT_MODE_JSON: {
      const pickedObject = {}
      for (const key of keys) {
        const value = sourceObject[ key ]
        verifyPick(key, value)
        pickedObject[ key ] = value
      }
      outputStringList.push(JSON.stringify(pickedObject))
      break
    }
    default:
      throw new Error(`[JSONPickLoader] invalid exportMode: ${String(exportMode)}`)
  }

  return outputStringList.join('\n')
}
const isBasicObject = (value) => (typeof value === 'object' && value !== null && !Array.isArray(value))
const verifyPick = (key, value) => { if (value === undefined) throw new Error(`[JSONPickLoader] source JSON missing key: ${String(key)}`) }

module.exports = JSONPickLoader

The sample usage:

config.module = { rules: [ {
  test: /package\.json$/,
  type: 'javascript/auto',
  use: { exportMode: 'export-both', loader: path.join(__dirname, 'webpack-json-pick-loader.js'), options: { keys: [ 'name', 'version' ] } }
} ] }
config.module = { rules: [ {
  test: /package\.json$/,
  use: { exportMode: 'json', loader: path.join(__dirname, 'webpack-json-pick-loader.js'), options: { keys: [ 'name', 'version' ] } }
} ] }

The updated test pack with loader exportMode set to export-both and json: test-webpack5-json-module-20201027.zip (with webpack@5.2.0)

And test log from npm t > output-test.log 2>&1:

[output-test.log]
> test-webpack5-json-module@0.0.0 test
> npm run build-test-0 && npm run build-test-1 && npm run build-test-2 && npm run build-test-3 && npm run build-test-4


> test-webpack5-json-module@0.0.0 build-test-0
> node build.js test-0-non-default-import.js

index: /root/TEST/test-webpack5-json-module/test-0-non-default-import.js
path: /root/TEST/test-webpack5-json-module
filename: output-test-0-non-default-import.js
{
  moduleIdentifier: '/root/TEST/test-webpack5-json-module/test-0-non-default-import.js',
  moduleName: './test-0-non-default-import.js',
  loc: '2:14-25',
  message: "Should not import the named export 'name' (imported as 'packageName') from default-exporting module (only default export is available soon)",
  details: '    at HarmonyImportSpecifierDependency.getLinkingErrors (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportDependency.js:136:7)\n' +
    '    at HarmonyImportSpecifierDependency._getErrors (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:177:15)\n' +
    '    at HarmonyImportSpecifierDependency.getWarnings (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:153:15)\n' +
    '    at Compilation.reportDependencyErrorsAndWarnings (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/Compilation.js:2065:24)\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/webpack/lib/Compilation.js:1704:10\n' +
    '    at _next2 (eval at create (/root/TEST/test-webpack5-json-module/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:25:1)\n' +
    '    at eval (eval at create (/root/TEST/test-webpack5-json-module/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:52:1)\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/webpack/lib/FlagDependencyExportsPlugin.js:332:11\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/neo-async/async.js:2830:7\n' +
    '    at Object.each (/root/TEST/test-webpack5-json-module/node_modules/neo-async/async.js:2850:39)'
}
{
  moduleIdentifier: '/root/TEST/test-webpack5-json-module/test-0-non-default-import.js',
  moduleName: './test-0-non-default-import.js',
  loc: '2:27-41',
  message: "Should not import the named export 'version' (imported as 'packageVersion') from default-exporting module (only default export is available soon)",
  details: '    at HarmonyImportSpecifierDependency.getLinkingErrors (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportDependency.js:136:7)\n' +
    '    at HarmonyImportSpecifierDependency._getErrors (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:177:15)\n' +
    '    at HarmonyImportSpecifierDependency.getWarnings (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:153:15)\n' +
    '    at Compilation.reportDependencyErrorsAndWarnings (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/Compilation.js:2065:24)\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/webpack/lib/Compilation.js:1704:10\n' +
    '    at _next2 (eval at create (/root/TEST/test-webpack5-json-module/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:25:1)\n' +
    '    at eval (eval at create (/root/TEST/test-webpack5-json-module/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:52:1)\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/webpack/lib/FlagDependencyExportsPlugin.js:332:11\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/neo-async/async.js:2830:7\n' +
    '    at Object.each (/root/TEST/test-webpack5-json-module/node_modules/neo-async/async.js:2850:39)'
}
[FILE_INPUT] test-0-non-default-import.js ==============================================================================
import { name as packageName, version as packageVersion } from './package.json'
console.log({ packageName, packageVersion })
[FILE_OUTPUT] output-test-0-non-default-import.js ======================================================================
(()=>{"use strict";const e=JSON.parse('{"u2":"test-webpack5-json-module","i8":"0.0.0"}');console.log({packageName:e.u2,packageVersion:e.i8})})();
[FILE_OUTPUT] output-test-0-non-default-import.js.loader-export-both.js ================================================
(()=>{"use strict";console.log({packageName:"test-webpack5-json-module",packageVersion:"0.0.0"})})();
{
  moduleIdentifier: '/root/TEST/test-webpack5-json-module/test-0-non-default-import.js',
  moduleName: './test-0-non-default-import.js',
  loc: '2:14-25',
  message: "Should not import the named export 'name' (imported as 'packageName') from default-exporting module (only default export is available soon)",
  details: '    at HarmonyImportSpecifierDependency.getLinkingErrors (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportDependency.js:136:7)\n' +
    '    at HarmonyImportSpecifierDependency._getErrors (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:177:15)\n' +
    '    at HarmonyImportSpecifierDependency.getWarnings (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:153:15)\n' +
    '    at Compilation.reportDependencyErrorsAndWarnings (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/Compilation.js:2065:24)\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/webpack/lib/Compilation.js:1704:10\n' +
    '    at _next2 (eval at create (/root/TEST/test-webpack5-json-module/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:25:1)\n' +
    '    at eval (eval at create (/root/TEST/test-webpack5-json-module/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:52:1)\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/webpack/lib/FlagDependencyExportsPlugin.js:332:11\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/neo-async/async.js:2830:7\n' +
    '    at Object.each (/root/TEST/test-webpack5-json-module/node_modules/neo-async/async.js:2850:39)'
}
{
  moduleIdentifier: '/root/TEST/test-webpack5-json-module/test-0-non-default-import.js',
  moduleName: './test-0-non-default-import.js',
  loc: '2:27-41',
  message: "Should not import the named export 'version' (imported as 'packageVersion') from default-exporting module (only default export is available soon)",
  details: '    at HarmonyImportSpecifierDependency.getLinkingErrors (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportDependency.js:136:7)\n' +
    '    at HarmonyImportSpecifierDependency._getErrors (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:177:15)\n' +
    '    at HarmonyImportSpecifierDependency.getWarnings (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:153:15)\n' +
    '    at Compilation.reportDependencyErrorsAndWarnings (/root/TEST/test-webpack5-json-module/node_modules/webpack/lib/Compilation.js:2065:24)\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/webpack/lib/Compilation.js:1704:10\n' +
    '    at _next2 (eval at create (/root/TEST/test-webpack5-json-module/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:25:1)\n' +
    '    at eval (eval at create (/root/TEST/test-webpack5-json-module/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:52:1)\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/webpack/lib/FlagDependencyExportsPlugin.js:332:11\n' +
    '    at /root/TEST/test-webpack5-json-module/node_modules/neo-async/async.js:2830:7\n' +
    '    at Object.each (/root/TEST/test-webpack5-json-module/node_modules/neo-async/async.js:2850:39)'
}
[FILE_OUTPUT] output-test-0-non-default-import.js.loader-json.js =======================================================
(()=>{"use strict";const e=JSON.parse('{"u":"test-webpack5-json-module","i":"0.0.0"}');console.log({packageName:e.u,packageVersion:e.i})})();
========================================================================================================================
[main] done

> test-webpack5-json-module@0.0.0 build-test-1
> node build.js test-1-default-import-basic-assignment.js

index: /root/TEST/test-webpack5-json-module/test-1-default-import-basic-assignment.js
path: /root/TEST/test-webpack5-json-module
filename: output-test-1-default-import-basic-assignment.js
[FILE_INPUT] test-1-default-import-basic-assignment.js =================================================================
import PACKAGE_JSON from './package.json'
const packageName = PACKAGE_JSON.name
const packageVersion = PACKAGE_JSON.version
console.log({ packageName, packageVersion })
[FILE_OUTPUT] output-test-1-default-import-basic-assignment.js =========================================================
(()=>{"use strict";const e=JSON.parse('{"u2":"test-webpack5-json-module","i8":"0.0.0"}'),s=e.u2,a=e.i8;console.log({packageName:s,packageVersion:a})})();
[FILE_OUTPUT] output-test-1-default-import-basic-assignment.js.loader-export-both.js ===================================
(()=>{"use strict";console.log({packageName:"test-webpack5-json-module",packageVersion:"0.0.0"})})();
[FILE_OUTPUT] output-test-1-default-import-basic-assignment.js.loader-json.js ==========================================
(()=>{"use strict";const e=JSON.parse('{"u":"test-webpack5-json-module","i":"0.0.0"}'),s=e.u,a=e.i;console.log({packageName:s,packageVersion:a})})();
========================================================================================================================
[main] done

> test-webpack5-json-module@0.0.0 build-test-2
> node build.js test-2-default-import-object-destructuring-assignment.js

index: /root/TEST/test-webpack5-json-module/test-2-default-import-object-destructuring-assignment.js
path: /root/TEST/test-webpack5-json-module
filename: output-test-2-default-import-object-destructuring-assignment.js
[FILE_INPUT] test-2-default-import-object-destructuring-assignment.js ==================================================
import PACKAGE_JSON from './package.json'
const { name: packageName, version: packageVersion } = PACKAGE_JSON
console.log({ packageName, packageVersion })
[FILE_OUTPUT] output-test-2-default-import-object-destructuring-assignment.js ==========================================
(()=>{"use strict";const e=JSON.parse('{"private":true,"name":"test-webpack5-json-module","version":"0.0.0","scripts":{"test":"npm run build-test-0 && npm run build-test-1 && npm run build-test-2 && npm run build-test-3 && npm run build-test-4","build-test-0":"node build.js test-0-non-default-import.js","build-test-1":"node build.js test-1-default-import-basic-assignment.js","build-test-2":"node build.js test-2-default-import-object-destructuring-assignment.js","build-test-3":"node build.js test-3-require-basic-assignment.js","build-test-4":"node build.js test-4-require-object-destructuring-assignment.js"},"devDependencies":{"webpack":"^5.2.0"}}'),{name:t,version:s}=e;console.log({packageName:t,packageVersion:s})})();
[FILE_OUTPUT] output-test-2-default-import-object-destructuring-assignment.js.loader-export-both.js ====================
(()=>{"use strict";const{name:e,version:o}={name:"test-webpack5-json-module",version:"0.0.0"};console.log({packageName:e,packageVersion:o})})();
[FILE_OUTPUT] output-test-2-default-import-object-destructuring-assignment.js.loader-json.js ===========================
(()=>{"use strict";const e=JSON.parse('{"name":"test-webpack5-json-module","version":"0.0.0"}'),{name:s,version:a}=e;console.log({packageName:s,packageVersion:a})})();
========================================================================================================================
[main] done

> test-webpack5-json-module@0.0.0 build-test-3
> node build.js test-3-require-basic-assignment.js

index: /root/TEST/test-webpack5-json-module/test-3-require-basic-assignment.js
path: /root/TEST/test-webpack5-json-module
filename: output-test-3-require-basic-assignment.js
[FILE_INPUT] test-3-require-basic-assignment.js ========================================================================
const PACKAGE_JSON = require('./package.json')
const packageName = PACKAGE_JSON.name
const packageVersion = PACKAGE_JSON.version
console.log({ packageName, packageVersion })
[FILE_OUTPUT] output-test-3-require-basic-assignment.js ================================================================
(()=>{var e={306:e=>{"use strict";e.exports=JSON.parse('{"private":true,"name":"test-webpack5-json-module","version":"0.0.0","scripts":{"test":"npm run build-test-0 && npm run build-test-1 && npm run build-test-2 && npm run build-test-3 && npm run build-test-4","build-test-0":"node build.js test-0-non-default-import.js","build-test-1":"node build.js test-1-default-import-basic-assignment.js","build-test-2":"node build.js test-2-default-import-object-destructuring-assignment.js","build-test-3":"node build.js test-3-require-basic-assignment.js","build-test-4":"node build.js test-4-require-object-destructuring-assignment.js"},"devDependencies":{"webpack":"^5.2.0"}}')}},t={};function s(n){if(t[n])return t[n].exports;var i=t[n]={exports:{}};return e[n](i,i.exports,s),i.exports}(()=>{const e=s(306),t=e.name,n=e.version;console.log({packageName:t,packageVersion:n})})()})();
[FILE_OUTPUT] output-test-3-require-basic-assignment.js.loader-export-both.js ==========================================
(()=>{var e={395:(e,o,r)=>{"use strict";r.r(o),r.d(o,{name:()=>t,version:()=>n,default:()=>a});const t="test-webpack5-json-module",n="0.0.0",a={name:t,version:n}}},o={};function r(t){if(o[t])return o[t].exports;var n=o[t]={exports:{}};return e[t](n,n.exports,r),n.exports}r.d=(e,o)=>{for(var t in o)r.o(o,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},r.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e=r(395),o=e.name,t=e.version;console.log({packageName:o,packageVersion:t})})()})();
[FILE_OUTPUT] output-test-3-require-basic-assignment.js.loader-json.js =================================================
(()=>{var e={395:e=>{"use strict";e.exports=JSON.parse('{"name":"test-webpack5-json-module","version":"0.0.0"}')}},r={};function o(s){if(r[s])return r[s].exports;var t=r[s]={exports:{}};return e[s](t,t.exports,o),t.exports}(()=>{const e=o(395),r=e.name,s=e.version;console.log({packageName:r,packageVersion:s})})()})();
========================================================================================================================
[main] done

> test-webpack5-json-module@0.0.0 build-test-4
> node build.js test-4-require-object-destructuring-assignment.js

index: /root/TEST/test-webpack5-json-module/test-4-require-object-destructuring-assignment.js
path: /root/TEST/test-webpack5-json-module
filename: output-test-4-require-object-destructuring-assignment.js
[FILE_INPUT] test-4-require-object-destructuring-assignment.js =========================================================
const { name: packagename, version: packageVersion } = require('./package.json')
console.log({ packageName, packageVersion })
[FILE_OUTPUT] output-test-4-require-object-destructuring-assignment.js =================================================
(()=>{var e={306:e=>{"use strict";e.exports=JSON.parse('{"private":true,"name":"test-webpack5-json-module","version":"0.0.0","scripts":{"test":"npm run build-test-0 && npm run build-test-1 && npm run build-test-2 && npm run build-test-3 && npm run build-test-4","build-test-0":"node build.js test-0-non-default-import.js","build-test-1":"node build.js test-1-default-import-basic-assignment.js","build-test-2":"node build.js test-2-default-import-object-destructuring-assignment.js","build-test-3":"node build.js test-3-require-basic-assignment.js","build-test-4":"node build.js test-4-require-object-destructuring-assignment.js"},"devDependencies":{"webpack":"^5.2.0"}}')}},t={};function s(n){if(t[n])return t[n].exports;var i=t[n]={exports:{}};return e[n](i,i.exports,s),i.exports}(()=>{const{name:e,version:t}=s(306);console.log({packageName,packageVersion:t})})()})();
[FILE_OUTPUT] output-test-4-require-object-destructuring-assignment.js.loader-export-both.js ===========================
(()=>{var e={395:(e,o,r)=>{"use strict";r.r(o),r.d(o,{name:()=>t,version:()=>n,default:()=>a});const t="test-webpack5-json-module",n="0.0.0",a={name:t,version:n}}},o={};function r(t){if(o[t])return o[t].exports;var n=o[t]={exports:{}};return e[t](n,n.exports,r),n.exports}r.d=(e,o)=>{for(var t in o)r.o(o,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},r.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const{name:e,version:o}=r(395);console.log({packageName,packageVersion:o})})()})();
[FILE_OUTPUT] output-test-4-require-object-destructuring-assignment.js.loader-json.js ==================================
(()=>{var e={395:e=>{"use strict";e.exports=JSON.parse('{"name":"test-webpack5-json-module","version":"0.0.0"}')}},r={};function o(s){if(r[s])return r[s].exports;var a=r[s]={exports:{}};return e[s](a,a.exports,o),a.exports}(()=>{const{name:e,version:r}=o(395);console.log({packageName,packageVersion:r})})()})();
========================================================================================================================
[main] done

From current test, seems it's better to match export with import (directly inlined value), and plain JSON with require (no module wrapper) for shorter code output.

dr-js added a commit to dr-js/dr-dev that referenced this issue Oct 27, 2020
notable change:
- add: `webpack-json-pick-loader` to filter `package.json` explicitly, related: webpack/webpack#11676
- fix: ci: enable color on GitHub Actions
- fix: `npxLazy` re-run and test
- script sort
- package update
dr-js added a commit to dr-js/dr-dev that referenced this issue Oct 29, 2020
notable change:
- break: use `@dr-js/*@0.4.0`
- break: use `webpack@5`
- break: use async `minifyFileWithTerser` instead of sync `minifyWithTerser` from `minify.js`
- break: limit eslint browser globals to window only
- add: `webpack-json-pick-loader` to filter `package.json` explicitly, related: webpack/webpack#11676
- add: `isPublishVerify` to `publishOutput`
- add: support `publish-auto`
- fix: `npxLazy` re-run and test
- fix: prevent clear whole pwd when omitting `fromOutput` of `clearOutput`
- fix: ci: enable color on GitHub Actions
- adjust `npxLazy` for `npm@7`: npm/cli@5473bbda
- enable new JSX transform: https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html
- script sort
- package update
@webpack-bot
Copy link
Contributor

This issue had no activity for at least three months.

It's subject to automatic issue closing if there is no activity in the next 15 days.

@alexander-akait
Copy link
Member

bump

@webpack-bot
Copy link
Contributor

Issue was closed because of inactivity.

If you think this is still a valid issue, please file a new issue with additional information.

@vankop
Copy link
Member

vankop commented Nov 23, 2021

Closing in favor of #14800

@vankop vankop closed this as completed Nov 23, 2021
@vankop vankop removed the parsing label Nov 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants