Skip to content

Commit 71bb4e8

Browse files
committed
feat webpack 5
1 parent 9ec1fc7 commit 71bb4e8

18 files changed

+14495
-3143
lines changed

package-lock.json

Lines changed: 14248 additions & 3039 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"scripts": {
1010
"build": "tsc && cp ./src/cjs.js ./lib",
1111
"lint": "eslint",
12-
"test:clean": "find -E test/**/build/ -regex '.*/*.(avif|jpg|webp|png|js)' -delete",
12+
"test:clean": "find -E test/**/build/ -regex '.*/*.(avif|jpg|jpeg|webp|png|js)' -delete",
1313
"test": "npm run build && npm run test:clean && webpack --config=./test/jimp/webpack.config.js && webpack --config=./test/sharp/webpack.config.js && webpack --config=./test/cache/webpack.config.js && jest"
1414
},
1515
"files": [
@@ -37,7 +37,7 @@
3737
},
3838
"homepage": "https://github.com/dazuaz/responsive-loader",
3939
"peerDependencies": {
40-
"webpack": "^4.0.0 || ^5.0.0"
40+
"webpack": "^5.37.0"
4141
},
4242
"peerDependenciesMeta": {
4343
"jimp": {
@@ -49,18 +49,17 @@
4949
},
5050
"dependencies": {
5151
"find-cache-dir": "^3.3.1",
52-
"loader-utils": "^2.0.0",
52+
"json5": "^2.2.0",
5353
"schema-utils": "^3.0.0"
5454
},
5555
"devDependencies": {
5656
"@babel/core": "^7.12.10",
5757
"@babel/preset-env": "^7.12.11",
5858
"@types/find-cache-dir": "^3.2.0",
5959
"@types/json-schema": "^7.0.6",
60-
"@types/loader-utils": "^2.0.1",
6160
"@types/node": "^14.14.20",
62-
"@types/sharp": "^0.27.0",
63-
"@types/webpack": "^4.41.25",
61+
"@types/sharp": "^0.28.1",
62+
"@types/webpack": "^5.28.0",
6463
"@typescript-eslint/eslint-plugin": "^4.12.0",
6564
"@typescript-eslint/parser": "^4.12.0",
6665
"babel-jest": "^26.6.3",
@@ -69,10 +68,10 @@
6968
"jimp": "^0.16.1",
7069
"prettier": "^2.2.1",
7170
"prettier-eslint": "^12.0.0",
72-
"sharp": "^0.27.0",
71+
"sharp": "^0.28.2",
7372
"typescript": "^4.1.3",
74-
"webpack": "^4.44.2",
75-
"webpack-cli": "^4.3.1"
73+
"webpack": "5.37.0",
74+
"webpack-cli": "^4.7.0"
7675
},
7776
"jest": {
7877
"testEnvironment": "node"

src/adapters/sharp.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import * as sharp from "sharp"
1+
import * as sharp from 'sharp'
22

33
type ResizeProps = {
44
width: number
5-
mime: "image/jpeg" | "image/png" | "image/webp" | "image/avif"
5+
mime: 'image/jpeg' | 'image/png' | 'image/webp' | 'image/avif'
66
options: {
77
background?: string
88
rotate: number
@@ -13,7 +13,7 @@ type ResizeProps = {
1313

1414
class SharpAdapter {
1515
image: sharp.Sharp
16-
constructor(imagePath: string | Buffer) {
16+
constructor(imagePath: string) {
1717
this.image = sharp(imagePath)
1818
}
1919
metadata(): Promise<sharp.Metadata> {
@@ -34,18 +34,18 @@ class SharpAdapter {
3434
})
3535
}
3636

37-
if (mime === "image/jpeg") {
37+
if (mime === 'image/jpeg') {
3838
resized = resized.jpeg({
3939
quality: options.quality,
4040
progressive: options.progressive,
4141
})
4242
}
43-
if (mime === "image/webp") {
43+
if (mime === 'image/webp') {
4444
resized = resized.webp({
4545
quality: options.quality,
4646
})
4747
}
48-
if (mime === "image/avif") {
48+
if (mime === 'image/avif') {
4949
// @ts-ignore
5050
resized = resized.avif({
5151
quality: options.quality,
@@ -55,7 +55,6 @@ class SharpAdapter {
5555
if (options.rotate && options.rotate !== 0) {
5656
resized = resized.rotate(options.rotate)
5757
}
58-
5958
resized.toBuffer((err, data, { height }) => {
6059
if (err) {
6160
reject(err)
@@ -71,6 +70,6 @@ class SharpAdapter {
7170
}
7271
}
7372
// export default SharpAdapter
74-
module.exports = (imagePath: string | Buffer): SharpAdapter => {
73+
module.exports = (imagePath: string): SharpAdapter => {
7574
return new SharpAdapter(imagePath)
7675
}

src/cjs.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const loader = require("./index")
1+
const loader = require('./index')
22

33
module.exports = loader.default
44
module.exports.raw = loader.raw

src/index.ts

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
import { parseQuery, getOptions, interpolateName } from 'loader-utils'
21
import { validate } from 'schema-utils'
32
import * as schema from './schema.json'
43
import { parseOptions, getOutputAndPublicPath, createPlaceholder } from './utils'
54
import { cache } from './cache'
5+
import type { LoaderContext } from 'webpack'
6+
7+
import { interpolateName } from './interpolateName'
8+
import { parseQuery } from './parseQuery'
69

710
import type {
811
Adapter,
912
Options,
1013
CacheOptions,
11-
LoaderContext,
1214
AdapterImplementation,
1315
MimeType,
1416
AdapterResizeResponse,
1517
TransformParams,
1618
} from './types'
19+
import { JSONSchema7 } from 'schema-utils/declarations/ValidationError'
1720

1821
const DEFAULTS = {
1922
quality: 85,
@@ -39,37 +42,30 @@ const DEFAULTS = {
3942
*
4043
* @return {loaderCallback} loaderCallback Result
4144
*/
42-
export default function loader(this: LoaderContext, content: Buffer): void {
45+
export default function loader(this: LoaderContext<any>, content: string): void {
4346
const loaderCallback = this.async()
4447
if (typeof loaderCallback == 'undefined') {
4548
new Error('Responsive loader callback error')
4649
return
4750
}
4851

49-
// Object representation of the query string
5052
const parsedResourceQuery = this.resourceQuery ? parseQuery(this.resourceQuery) : {}
51-
5253
// Combines defaults, webpack options and query options,
5354
// later sources' properties overwrite earlier ones.
54-
const options: Options = Object.assign({}, DEFAULTS, getOptions(this), parsedResourceQuery)
55+
const options: Options = Object.assign({}, DEFAULTS, this.getOptions(), parsedResourceQuery)
56+
// // Object representation of the query string
5557

56-
// @ts-ignore
57-
validate(schema, options, { name: 'Responsive Loader' })
58+
// // Combines defaults, webpack options and query options,
59+
// // later sources' properties overwrite earlier ones.
60+
// const options: Options = Object.assign({}, DEFAULTS, getOptions(this), parsedResourceQuery)
61+
62+
validate(schema as JSONSchema7, options, { name: 'Responsive Loader' })
5863

5964
/**
6065
* Parses options and set defaults options
6166
*/
62-
const {
63-
outputContext,
64-
mime,
65-
ext,
66-
name,
67-
sizes,
68-
outputPlaceholder,
69-
placeholderSize,
70-
imageOptions,
71-
cacheOptions,
72-
} = parseOptions(this, options)
67+
const { outputContext, mime, ext, name, sizes, outputPlaceholder, placeholderSize, imageOptions, cacheOptions } =
68+
parseOptions(this, options)
7369

7470
if (!mime) {
7571
loaderCallback(new Error('No mime type for file with extension ' + ext + ' supported'))
@@ -90,7 +86,7 @@ export default function loader(this: LoaderContext, content: Buffer): void {
9086
})
9187

9288
if (options.emitFile) {
93-
this.emitFile(outputPath, data, null)
89+
this.emitFile(outputPath, data)
9490
}
9591

9692
return {

src/interpolateName.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
'use strict'
2+
import type { LoaderContext } from 'webpack'
3+
import { util } from 'webpack'
4+
import * as path from 'path'
5+
6+
type Options = {
7+
context: string
8+
content: string
9+
}
10+
11+
function interpolateName(loaderContext: LoaderContext<any>, name: string, options: Options): string {
12+
const filename = name || '[hash].[ext]'
13+
14+
const context = options.context
15+
const content = options.content
16+
17+
let ext = 'bin'
18+
let basename = 'file'
19+
let directory = ''
20+
let folder = ''
21+
let query = ''
22+
23+
if (loaderContext.resourcePath) {
24+
const parsed = path.parse(loaderContext.resourcePath)
25+
let resourcePath = loaderContext.resourcePath
26+
27+
if (parsed.ext) {
28+
ext = parsed.ext.substr(1)
29+
}
30+
31+
if (parsed.dir) {
32+
basename = parsed.name
33+
resourcePath = parsed.dir + path.sep
34+
}
35+
36+
if (typeof context !== 'undefined') {
37+
directory = path
38+
.relative(context, resourcePath + '_')
39+
.replace(/\\/g, '/')
40+
.replace(/\.\.(\/)?/g, '_$1')
41+
directory = directory.substr(0, directory.length - 1)
42+
} else {
43+
directory = resourcePath.replace(/\\/g, '/').replace(/\.\.(\/)?/g, '_$1')
44+
}
45+
46+
if (directory.length === 1) {
47+
directory = ''
48+
} else if (directory.length > 1) {
49+
folder = path.basename(directory)
50+
}
51+
}
52+
53+
if (loaderContext.resourceQuery && loaderContext.resourceQuery.length > 1) {
54+
query = loaderContext.resourceQuery
55+
56+
const hashIdx = query.indexOf('#')
57+
58+
if (hashIdx >= 0) {
59+
query = query.substr(0, hashIdx)
60+
}
61+
}
62+
63+
let url = filename
64+
65+
if (content) {
66+
const hash = util.createHash('md4')
67+
hash.update(content)
68+
// Match hash template
69+
url = url
70+
// `hash` and `contenthash` are same in `loader-utils` context
71+
// let's keep `hash` for backward compatibility
72+
.replace(/\[(?:([^:\]]+):)?(?:hash|contenthash)(?::([a-z]+\d*))?(?::(\d+))?\]/gi, `${hash.digest('hex')}`)
73+
}
74+
75+
url = url
76+
.replace(/\[ext\]/gi, () => ext)
77+
.replace(/\[name\]/gi, () => basename)
78+
.replace(/\[path\]/gi, () => directory)
79+
.replace(/\[folder\]/gi, () => folder)
80+
.replace(/\[query\]/gi, () => query)
81+
82+
// if (
83+
// typeof loaderContext.getOptions() === 'object' &&
84+
// typeof loaderContext.getOptions().customInterpolateName === 'function'
85+
// ) {
86+
// url = loaderContext.getOptions().customInterpolateName.call(loaderContext, url, name, options)
87+
// }
88+
89+
return url
90+
}
91+
92+
export { interpolateName }

src/parseQuery.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
'use strict'
2+
3+
import * as JSON5 from 'json5'
4+
5+
interface LooseObject {
6+
[key: string]: any
7+
}
8+
9+
const specialValues: LooseObject = {
10+
null: null,
11+
true: true,
12+
false: false,
13+
}
14+
15+
function parseQuery(query: string): LooseObject {
16+
if (query.substr(0, 1) !== '?') {
17+
throw new Error("A valid query string passed to parseQuery should begin with '?'")
18+
}
19+
20+
query = query.substr(1)
21+
22+
if (!query) {
23+
return {}
24+
}
25+
26+
if (query.substr(0, 1) === '{' && query.substr(-1) === '}') {
27+
return JSON5.parse(query)
28+
}
29+
30+
const queryArgs = query.split(/[,&]/g)
31+
const result: LooseObject = {}
32+
33+
queryArgs.forEach((arg) => {
34+
const idx = arg.indexOf('=')
35+
36+
if (idx >= 0) {
37+
let name = arg.substr(0, idx)
38+
let value = decodeURIComponent(arg.substr(idx + 1))
39+
// const specialValues: LooseObject = {}
40+
41+
// eslint-disable-next-line no-prototype-builtins
42+
if (specialValues.hasOwnProperty(value)) {
43+
value = specialValues[value]
44+
}
45+
if (name.substr(-2) === '[]') {
46+
name = decodeURIComponent(name.substr(0, name.length - 2))
47+
48+
if (!Array.isArray(result[name])) {
49+
result[name] = []
50+
}
51+
52+
result[name].push(value)
53+
} else {
54+
name = decodeURIComponent(name)
55+
result[name] = value
56+
}
57+
} else {
58+
if (arg.substr(0, 1) === '-') {
59+
result[decodeURIComponent(arg.substr(1))] = false
60+
} else if (arg.substr(0, 1) === '+') {
61+
result[decodeURIComponent(arg.substr(1))] = true
62+
} else {
63+
result[decodeURIComponent(arg)] = true
64+
}
65+
}
66+
})
67+
68+
return result
69+
}
70+
export { parseQuery }

0 commit comments

Comments
 (0)