Skip to content

Commit 8ef0816

Browse files
committed
webpack 5
2 parents 0006109 + d8f060c commit 8ef0816

32 files changed

+17365
-9809
lines changed

.babelrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"@babel/preset-env",
55
{
66
"targets": {
7-
"node": "10.13.0"
7+
"node": "12.22.1"
88
}
99
}
1010
]

package-lock.json

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

package.json

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
"description": "A webpack loader for responsive images",
55
"main": "lib/cjs.js",
66
"engines": {
7-
"node": ">= 10.13.0"
7+
"node": ">= 12.22.1"
88
},
99
"scripts": {
1010
"build": "tsc && cp ./src/cjs.js ./lib",
1111
"lint": "eslint",
12-
"test:clean": "find test/**/build/ -name '*.jpg' -o -name '*.png' -o -name '*.avif' -o -name '*.webp' -o -name '*.js' | xargs rm -f",
13-
"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"
12+
"test:clean": "find test/**/build/ -name '*.jpg' -o -name '*.png' -o -name '*.avif' -o -name '*.wep' -o -name '*.js' | xargs rm -f",
13+
"test": "npm run build && npm run test:clean && webpack --config=./test/jimp/webpack.config.js && webpack --config=./test/sharp/webpack.config.js && jest"
1414
},
1515
"files": [
1616
"lib",
@@ -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.38.1"
4141
},
4242
"peerDependenciesMeta": {
4343
"jimp": {
@@ -48,31 +48,31 @@
4848
}
4949
},
5050
"dependencies": {
51-
"find-cache-dir": "^3.3.1",
52-
"loader-utils": "^2.0.0",
53-
"schema-utils": "^3.0.0"
51+
"@types/node": "^14.17.34",
52+
"find-cache-dir": "^3.3.2",
53+
"json5": "^2.2.0",
54+
"schema-utils": "^4.0.0"
5455
},
5556
"devDependencies": {
56-
"@babel/core": "^7.12.10",
57-
"@babel/preset-env": "^7.12.11",
58-
"@types/find-cache-dir": "^3.2.0",
59-
"@types/json-schema": "^7.0.6",
60-
"@types/loader-utils": "^2.0.1",
61-
"@types/node": "^14.14.20",
62-
"@types/sharp": "^0.27.0",
63-
"@types/webpack": "^4.41.25",
64-
"@typescript-eslint/eslint-plugin": "^4.12.0",
65-
"@typescript-eslint/parser": "^4.12.0",
66-
"babel-jest": "^26.6.3",
67-
"eslint": "^7.17.0",
68-
"jest": "^26.6.3",
57+
"@babel/core": "^7.16.0",
58+
"@babel/preset-env": "^7.16.4",
59+
"@types/find-cache-dir": "^3.2.1",
60+
"@types/jest": "^27.0.3",
61+
"@types/json-schema": "^7.0.9",
62+
"@types/sharp": "^0.29.4",
63+
"@types/webpack": "^5.28.0",
64+
"@typescript-eslint/eslint-plugin": "^5.5.0",
65+
"@typescript-eslint/parser": "^5.5.0",
66+
"babel-jest": "^27.4.2",
67+
"eslint": "^8.3.0",
68+
"jest": "^27.4.3",
6969
"jimp": "^0.16.1",
70-
"prettier": "^2.2.1",
71-
"prettier-eslint": "^12.0.0",
72-
"sharp": "^0.27.0",
73-
"typescript": "^4.1.3",
74-
"webpack": "^4.44.2",
75-
"webpack-cli": "^4.3.1"
70+
"prettier": "^2.5.0",
71+
"prettier-eslint": "^13.0.0",
72+
"sharp": "^0.29.3",
73+
"typescript": "^4.5.2",
74+
"webpack": "^5.64.4",
75+
"webpack-cli": "^4.9.1"
7676
},
7777
"jest": {
7878
"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: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
import { parseQuery, getOptions, interpolateName } from 'loader-utils'
2-
import { validate } from 'schema-utils'
31
import * as schema from './schema.json'
2+
import { validate } from 'schema-utils'
3+
import { JSONSchema7 } from 'schema-utils/declarations/ValidationError'
4+
45
import { parseOptions, getOutputAndPublicPath, createPlaceholder } from './utils'
56
import { cache } from './cache'
7+
import type { LoaderContext } from 'webpack'
8+
9+
import interpolateName from './interpolateName'
10+
import { parseQuery } from './parseQuery'
611

712
import type {
813
Adapter,
914
Options,
1015
CacheOptions,
11-
LoaderContext,
1216
AdapterImplementation,
1317
MimeType,
1418
AdapterResizeResponse,
@@ -39,47 +43,36 @@ const DEFAULTS = {
3943
*
4044
* @return {loaderCallback} loaderCallback Result
4145
*/
42-
export default function loader(this: LoaderContext, content: Buffer): void {
46+
export default function loader(this: LoaderContext<Options>, content: string): void {
4347
const loaderCallback = this.async()
4448
if (typeof loaderCallback == 'undefined') {
4549
new Error('Responsive loader callback error')
4650
return
4751
}
4852

49-
// Object representation of the query string
53+
// Parsers the query string and options
5054
const parsedResourceQuery = this.resourceQuery ? parseQuery(this.resourceQuery) : {}
5155

5256
// Combines defaults, webpack options and query options,
53-
// later sources' properties overwrite earlier ones.
54-
const options: Options = Object.assign({}, DEFAULTS, getOptions(this), parsedResourceQuery)
57+
const options = { ...DEFAULTS, ...this.getOptions(), ...parsedResourceQuery }
5558

56-
// @ts-ignore
57-
validate(schema, options, { name: 'Responsive Loader' })
59+
validate(schema as JSONSchema7, options, { name: 'Responsive Loader' })
5860

59-
/**
60-
* Parses options and set defaults options
61-
*/
62-
const {
63-
outputContext,
64-
mime,
65-
ext,
66-
name,
67-
sizes,
68-
outputPlaceholder,
69-
placeholderSize,
70-
imageOptions,
71-
cacheOptions,
72-
} = parseOptions(this, options)
61+
const outputContext = options.context || this.rootContext
62+
const { mime, ext, name, sizes, outputPlaceholder, placeholderSize, imageOptions, cacheOptions } = parseOptions(
63+
this.resourcePath,
64+
options
65+
)
7366

7467
if (!mime) {
7568
loaderCallback(new Error('No mime type for file with extension ' + ext + ' supported'))
7669
return
7770
}
7871

7972
const createFile = ({ data, width, height }: AdapterResizeResponse) => {
80-
const fileName = interpolateName(this, name, {
73+
const fileName = interpolateName(this.resourcePath, this.resourceQuery, name, {
8174
context: outputContext,
82-
content: data,
75+
content: data.toString(),
8376
})
8477
.replace(/\[width\]/gi, width + '')
8578
.replace(/\[height\]/gi, height + '')
@@ -90,7 +83,7 @@ export default function loader(this: LoaderContext, content: Buffer): void {
9083
})
9184

9285
if (options.emitFile) {
93-
this.emitFile(outputPath, data, null)
86+
this.emitFile(outputPath, data)
9487
}
9588

9689
return {
@@ -113,16 +106,14 @@ export default function loader(this: LoaderContext, content: Buffer): void {
113106
images: [{path:${path},width:100,height:100}],
114107
src: ${path},
115108
toString: function(){return ${path}}
116-
};`
109+
}`
117110
)
118111
return
119112
}
120-
/**
121-
* The full config is passed to the adapter, later sources' properties overwrite earlier ones.
122-
*/
113+
// The full config is passed to the adapter, later sources' properties overwrite earlier ones.
123114
const adapterOptions = Object.assign({}, options, imageOptions)
124115

125-
const transformParams: TransformParams = {
116+
const transformParams = {
126117
adapterModule: options.adapter,
127118
resourcePath: this.resourcePath,
128119
adapterOptions,
@@ -158,8 +149,8 @@ async function orchestrate(params: OrchestrateParams) {
158149
// Transform based on the parameters
159150
export async function transform({
160151
adapterModule,
161-
createFile,
162152
resourcePath,
153+
createFile,
163154
sizes,
164155
mime,
165156
outputPlaceholder,
@@ -183,16 +174,16 @@ export async function transform({
183174

184175
const srcset = files.map((f) => f.src).join('+","+')
185176
const images = files.map((f) => `{path: ${f.path},width: ${f.width},height: ${f.height}}`).join(',')
186-
const firstImage = files[0]
177+
const defaultImage = outputPlaceholder ? files[files.length - 2] : files[files.length - 1]
187178

188179
return `${esModule ? 'export default' : 'module.exports ='} {
189180
srcSet: ${srcset},
190181
images: [${images}],
191-
src: ${firstImage.path},
192-
toString: function(){return ${firstImage.path}},
182+
src: ${defaultImage.path},
183+
toString: function(){return ${defaultImage.path}},
193184
${placeholder ? 'placeholder: ' + placeholder + ',' : ''}
194-
width: ${firstImage.width},
195-
height: ${firstImage.height}
185+
width: ${defaultImage.width},
186+
height: ${defaultImage.height}
196187
}`
197188
}
198189

src/interpolateName.ts

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

0 commit comments

Comments
 (0)