@place-framework/place-block-image 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -0
- package/dist/constants/index.d.ts +9 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +17 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/react-jsx.d.ts +2 -0
- package/dist/templates/react-jsx.d.ts.map +1 -0
- package/dist/templates/react-jsx.js +29 -0
- package/dist/templates/react-jsx.js.map +1 -0
- package/dist/templates/react-tsx.d.ts +2 -0
- package/dist/templates/react-tsx.d.ts.map +1 -0
- package/dist/templates/react-tsx.js +35 -0
- package/dist/templates/react-tsx.js.map +1 -0
- package/dist/templates/shared/index.d.ts +6 -0
- package/dist/templates/shared/index.d.ts.map +1 -0
- package/dist/templates/shared/index.js +49 -0
- package/dist/templates/shared/index.js.map +1 -0
- package/dist/templates/shared/react.d.ts +10 -0
- package/dist/templates/shared/react.d.ts.map +1 -0
- package/dist/templates/shared/react.js +48 -0
- package/dist/templates/shared/react.js.map +1 -0
- package/dist/templates/vue.d.ts +2 -0
- package/dist/templates/vue.d.ts.map +1 -0
- package/dist/templates/vue.js +100 -0
- package/dist/templates/vue.js.map +1 -0
- package/dist/templates.d.ts +5 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +32 -0
- package/dist/templates.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +32 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/webpack-plugin.d.ts +49 -0
- package/dist/webpack-plugin.d.ts.map +1 -0
- package/dist/webpack-plugin.js +259 -0
- package/dist/webpack-plugin.js.map +1 -0
- package/package.json +49 -0
- package/src/constants/index.ts +14 -0
- package/src/index.ts +4 -0
- package/src/templates/react-jsx.ts +27 -0
- package/src/templates/react-tsx.ts +33 -0
- package/src/templates/shared/index.ts +47 -0
- package/src/templates/shared/react.ts +51 -0
- package/src/templates/vue.ts +98 -0
- package/src/templates.ts +29 -0
- package/src/utils/index.ts +35 -0
- package/src/webpack-plugin.ts +273 -0
- package/tsconfig.json +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# @place-framework/place-block-image
|
|
2
|
+
|
|
3
|
+
A webpack plugin that generates CSS custom properties from image dimensions to prevent layout shift and automatically creates components for React and Vue.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides a webpack plugin that:
|
|
8
|
+
|
|
9
|
+
- Scans your project for images
|
|
10
|
+
- Extracts image dimensions automatically
|
|
11
|
+
- Generates CSS custom properties (CSS variables) for width, height, and aspect ratio
|
|
12
|
+
- Creates optimized components for React and Vue frameworks
|
|
13
|
+
- Prevents cumulative layout shift (CLS) by providing image dimensions upfront
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Automatic Image Processing**: Automatically detects and processes images in your project
|
|
18
|
+
- **CSS Custom Properties**: Generates CSS variables for image dimensions
|
|
19
|
+
- **Framework Components**: Creates ready-to-use components for React (JSX/TSX) and Vue
|
|
20
|
+
- **Layout Shift Prevention**: Helps maintain stable layouts by providing image dimensions
|
|
21
|
+
- **TypeScript Support**: Full TypeScript support with type definitions
|
|
22
|
+
- **Webpack Integration**: Seamless integration with your webpack build process
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @place-framework/place-block-image
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
### Webpack Configuration
|
|
33
|
+
|
|
34
|
+
Add the plugin to your webpack configuration:
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
const PlaceBlockImagePlugin = require('@place-framework/place-block-image');
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
// ... your webpack config
|
|
41
|
+
plugins: [
|
|
42
|
+
new PlaceBlockImagePlugin({
|
|
43
|
+
// Plugin options
|
|
44
|
+
})
|
|
45
|
+
]
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Generated CSS Variables
|
|
50
|
+
|
|
51
|
+
The plugin generates CSS custom properties like:
|
|
52
|
+
|
|
53
|
+
```css
|
|
54
|
+
:root {
|
|
55
|
+
--image-example-width: 800px;
|
|
56
|
+
--image-example-height: 600px;
|
|
57
|
+
--image-example-aspect-ratio: 1.333;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Generated Components
|
|
62
|
+
|
|
63
|
+
#### React (JSX/TSX)
|
|
64
|
+
```jsx
|
|
65
|
+
import { ImageExample } from './generated/images';
|
|
66
|
+
|
|
67
|
+
function MyComponent() {
|
|
68
|
+
return <ImageExample alt="Example image" />;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### Vue
|
|
73
|
+
```vue
|
|
74
|
+
<template>
|
|
75
|
+
<ImageExample alt="Example image" />
|
|
76
|
+
</template>
|
|
77
|
+
|
|
78
|
+
<script>
|
|
79
|
+
import { ImageExample } from './generated/images';
|
|
80
|
+
|
|
81
|
+
export default {
|
|
82
|
+
components: {
|
|
83
|
+
ImageExample
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
</script>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Benefits
|
|
90
|
+
|
|
91
|
+
- **Performance**: Prevents layout shift by providing image dimensions upfront
|
|
92
|
+
- **Developer Experience**: Automatically generates components, reducing boilerplate
|
|
93
|
+
- **Maintainability**: Centralized image dimension management
|
|
94
|
+
- **SEO**: Better Core Web Vitals scores through CLS prevention
|
|
95
|
+
|
|
96
|
+
## Configuration Options
|
|
97
|
+
|
|
98
|
+
The plugin accepts various configuration options to customize its behavior:
|
|
99
|
+
|
|
100
|
+
- Image source directories
|
|
101
|
+
- Output paths for generated files
|
|
102
|
+
- Component template customization
|
|
103
|
+
- CSS variable naming conventions
|
|
104
|
+
|
|
105
|
+
## Requirements
|
|
106
|
+
|
|
107
|
+
- **Webpack**: >= 5.0.0
|
|
108
|
+
- **React**: >= 16.8.0 (optional, for React components)
|
|
109
|
+
- **Vue**: >= 3.0.0 (optional, for Vue components)
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
MIT
|
|
114
|
+
|
|
115
|
+
## Author
|
|
116
|
+
|
|
117
|
+
Brian Kelley
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const CLASS_NAMES: {
|
|
2
|
+
readonly LAZY: "lazy";
|
|
3
|
+
readonly LOADED: "loaded";
|
|
4
|
+
readonly IMAGE_WRAPPER: "image-wrapper";
|
|
5
|
+
readonly IMAGE_BLOCK: "image-block";
|
|
6
|
+
};
|
|
7
|
+
export declare const getWrapperClassName: (imagePrefix: string) => string;
|
|
8
|
+
export declare const getImageClassName: (imagePrefix: string, filename: string) => string;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW;;;;;CAUd,CAAC;AAEX,eAAO,MAAM,mBAAmB,GAAI,aAAa,MAAM,WAA4B,CAAC;AACpF,eAAO,MAAM,iBAAiB,GAAI,aAAa,MAAM,EAAE,UAAU,MAAM,WAAgC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getImageClassName = exports.getWrapperClassName = exports.CLASS_NAMES = void 0;
|
|
4
|
+
exports.CLASS_NAMES = {
|
|
5
|
+
// Lazy loading states
|
|
6
|
+
LAZY: 'lazy',
|
|
7
|
+
LOADED: 'loaded',
|
|
8
|
+
// Wrapper classes
|
|
9
|
+
IMAGE_WRAPPER: 'image-wrapper',
|
|
10
|
+
// Base image class
|
|
11
|
+
IMAGE_BLOCK: 'image-block'
|
|
12
|
+
};
|
|
13
|
+
const getWrapperClassName = (imagePrefix) => `${imagePrefix}wrapper`;
|
|
14
|
+
exports.getWrapperClassName = getWrapperClassName;
|
|
15
|
+
const getImageClassName = (imagePrefix, filename) => `${imagePrefix}${filename}`;
|
|
16
|
+
exports.getImageClassName = getImageClassName;
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":";;;AAAa,QAAA,WAAW,GAAG;IACzB,sBAAsB;IACtB,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,QAAQ;IAEhB,kBAAkB;IAClB,aAAa,EAAE,eAAe;IAE9B,mBAAmB;IACnB,WAAW,EAAE,aAAa;CAClB,CAAC;AAEJ,MAAM,mBAAmB,GAAG,CAAC,WAAmB,EAAE,EAAE,CAAC,GAAG,WAAW,SAAS,CAAC;AAAvE,QAAA,mBAAmB,uBAAoD;AAC7E,MAAM,iBAAiB,GAAG,CAAC,WAAmB,EAAE,QAAgB,EAAE,EAAE,CAAC,GAAG,WAAW,GAAG,QAAQ,EAAE,CAAC;AAA3F,QAAA,iBAAiB,qBAA0E"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,YAAY,EAAE,4BAA4B,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Main exports for place-block-image package
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.PlaceBlockImagePlugin = void 0;
|
|
5
|
+
var webpack_plugin_1 = require("./webpack-plugin");
|
|
6
|
+
Object.defineProperty(exports, "PlaceBlockImagePlugin", { enumerable: true, get: function () { return webpack_plugin_1.PlaceBlockImagePlugin; } });
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,6CAA6C;;;AAE7C,mDAAyD;AAAhD,uHAAA,qBAAqB,OAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-jsx.d.ts","sourceRoot":"","sources":["../../src/templates/react-jsx.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAwB/D"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getReactJsxTemplate = getReactJsxTemplate;
|
|
4
|
+
const react_1 = require("./shared/react");
|
|
5
|
+
function getReactJsxTemplate(imagePrefix) {
|
|
6
|
+
const shared = (0, react_1.getSharedReactTemplate)(imagePrefix);
|
|
7
|
+
return `${shared.imports}
|
|
8
|
+
|
|
9
|
+
${shared.comment}
|
|
10
|
+
export const PlaceBlockImage = ({
|
|
11
|
+
src,
|
|
12
|
+
alt,
|
|
13
|
+
lazy = false,
|
|
14
|
+
className = '',
|
|
15
|
+
...props
|
|
16
|
+
}) => {
|
|
17
|
+
${shared.hooks}
|
|
18
|
+
|
|
19
|
+
${shared.getImageClassName}
|
|
20
|
+
|
|
21
|
+
${shared.classNames}
|
|
22
|
+
|
|
23
|
+
${shared.jsx}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
${shared.export}
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=react-jsx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-jsx.js","sourceRoot":"","sources":["../../src/templates/react-jsx.ts"],"names":[],"mappings":";;AAEA,kDAwBC;AA1BD,0CAAwD;AAExD,SAAgB,mBAAmB,CAAC,WAAmB;IACrD,MAAM,MAAM,GAAG,IAAA,8BAAsB,EAAC,WAAW,CAAC,CAAC;IAEnD,OAAO,GAAG,MAAM,CAAC,OAAO;;EAExB,MAAM,CAAC,OAAO;;;;;;;;EAQd,MAAM,CAAC,KAAK;;EAEZ,MAAM,CAAC,iBAAiB;;EAExB,MAAM,CAAC,UAAU;;EAEjB,MAAM,CAAC,GAAG;;;EAGV,MAAM,CAAC,MAAM;CACd,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-tsx.d.ts","sourceRoot":"","sources":["../../src/templates/react-tsx.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA8B/D"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getReactTsxTemplate = getReactTsxTemplate;
|
|
4
|
+
const react_1 = require("./shared/react");
|
|
5
|
+
function getReactTsxTemplate(imagePrefix) {
|
|
6
|
+
const shared = (0, react_1.getSharedReactTemplate)(imagePrefix);
|
|
7
|
+
return `${shared.imports}
|
|
8
|
+
|
|
9
|
+
interface PlaceBlockImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
|
|
10
|
+
src: string;
|
|
11
|
+
alt: string;
|
|
12
|
+
lazy?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
${shared.comment}
|
|
16
|
+
export const PlaceBlockImage: React.FC<PlaceBlockImageProps> = ({
|
|
17
|
+
src,
|
|
18
|
+
alt,
|
|
19
|
+
lazy = false,
|
|
20
|
+
className = '',
|
|
21
|
+
...props
|
|
22
|
+
}) => {
|
|
23
|
+
${shared.hooks}
|
|
24
|
+
|
|
25
|
+
${shared.getImageClassName}
|
|
26
|
+
|
|
27
|
+
${shared.classNames}
|
|
28
|
+
|
|
29
|
+
${shared.jsx}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
${shared.export}
|
|
33
|
+
`;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=react-tsx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-tsx.js","sourceRoot":"","sources":["../../src/templates/react-tsx.ts"],"names":[],"mappings":";;AAEA,kDA8BC;AAhCD,0CAAwD;AAExD,SAAgB,mBAAmB,CAAC,WAAmB;IACrD,MAAM,MAAM,GAAG,IAAA,8BAAsB,EAAC,WAAW,CAAC,CAAC;IAEnD,OAAO,GAAG,MAAM,CAAC,OAAO;;;;;;;;EAQxB,MAAM,CAAC,OAAO;;;;;;;;EAQd,MAAM,CAAC,KAAK;;EAEZ,MAAM,CAAC,iBAAiB;;EAExB,MAAM,CAAC,UAAU;;EAEjB,MAAM,CAAC,GAAG;;;EAGV,MAAM,CAAC,MAAM;CACd,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/shared/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,GAAI,aAAa,MAAM;;;;CA4CnD,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSharedTemplate = void 0;
|
|
4
|
+
const constants_1 = require("../../constants");
|
|
5
|
+
const getSharedTemplate = (imagePrefix) => ({
|
|
6
|
+
// Common comment block
|
|
7
|
+
comment: `/**
|
|
8
|
+
* PlaceBlockImage component that prevents layout shift using CSS custom properties
|
|
9
|
+
* Generated by place-block-image webpack plugin
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* <PlaceBlockImage src="/images/logo.svg" alt="Logo" />
|
|
13
|
+
* <PlaceBlockImage src="/images/hero.jpg" alt="Hero" lazy={true} />
|
|
14
|
+
*
|
|
15
|
+
* This will automatically apply:
|
|
16
|
+
* - .${imagePrefix}wrapper class on picture (for dimensions)
|
|
17
|
+
* - .${imagePrefix}logo class on picture (specific dimensions via CSS custom properties)
|
|
18
|
+
* - ${constants_1.CLASS_NAMES.LAZY}/${constants_1.CLASS_NAMES.LAZY}.${constants_1.CLASS_NAMES.LOADED} classes for lazy loading states
|
|
19
|
+
*/`,
|
|
20
|
+
// Common filename extraction logic (as string for interpolation)
|
|
21
|
+
getImageClassNameTemplate: `// Extract filename from src to generate class name
|
|
22
|
+
const getImageClassName = (imageSrc: string): string => {
|
|
23
|
+
// Remove /images/ prefix and file extension, convert to kebab-case
|
|
24
|
+
const filename = imageSrc
|
|
25
|
+
.replace(/^.*\\/images\\//, '') // Remove path up to /images/
|
|
26
|
+
.replace(/\\.[^/.]+$/, '') // Remove file extension
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.replace(/[^a-z0-9-]/g, '-') // Convert special chars to hyphens
|
|
29
|
+
.replace(/-+/g, '-') // Remove duplicate hyphens
|
|
30
|
+
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
|
31
|
+
|
|
32
|
+
return \`${imagePrefix}\${filename}\`;
|
|
33
|
+
};`,
|
|
34
|
+
// Common intersection observer logic
|
|
35
|
+
intersectionObserverTemplate: `const observer = new IntersectionObserver(
|
|
36
|
+
(entries) => {
|
|
37
|
+
entries.forEach((entry) => {
|
|
38
|
+
if (entry.isIntersecting) {
|
|
39
|
+
setImageSrc(src);
|
|
40
|
+
setIsLoaded(true);
|
|
41
|
+
observer.unobserve(entry.target);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
{ threshold: 0.1 }
|
|
46
|
+
);`
|
|
47
|
+
});
|
|
48
|
+
exports.getSharedTemplate = getSharedTemplate;
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/shared/index.ts"],"names":[],"mappings":";;;AAAA,+CAA8C;AAEvC,MAAM,iBAAiB,GAAG,CAAC,WAAmB,EAAE,EAAE,CAAC,CAAC;IACzD,uBAAuB;IACvB,OAAO,EAAE;;;;;;;;;QASH,WAAW;QACX,WAAW;OACZ,uBAAW,CAAC,IAAI,IAAI,uBAAW,CAAC,IAAI,IAAI,uBAAW,CAAC,MAAM;IAC7D;IAEF,iEAAiE;IACjE,yBAAyB,EAAE;;;;;;;;;;;eAWd,WAAW;KACrB;IAEH,qCAAqC;IACrC,4BAA4B,EAAE;;;;;;;;;;;OAWzB;CACN,CAAC,CAAC;AA5CU,QAAA,iBAAiB,qBA4C3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../../src/templates/shared/react.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,sBAAsB,GAAI,aAAa,MAAM;;;;;;;;CA+CzD,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSharedReactTemplate = void 0;
|
|
4
|
+
const index_1 = require("./index");
|
|
5
|
+
const constants_1 = require("../../constants");
|
|
6
|
+
const getSharedReactTemplate = (imagePrefix) => {
|
|
7
|
+
const shared = (0, index_1.getSharedTemplate)(imagePrefix);
|
|
8
|
+
return {
|
|
9
|
+
imports: `import React, { useRef, useEffect, useState } from 'react';`,
|
|
10
|
+
comment: shared.comment,
|
|
11
|
+
hooks: ` const imgRef = useRef(null);
|
|
12
|
+
const [imageSrc, setImageSrc] = useState(lazy ? '' : src);
|
|
13
|
+
const [isLoaded, setIsLoaded] = useState(!lazy);
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!lazy || isLoaded) return;
|
|
17
|
+
|
|
18
|
+
${shared.intersectionObserverTemplate}
|
|
19
|
+
|
|
20
|
+
if (imgRef.current) {
|
|
21
|
+
observer.observe(imgRef.current);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return () => observer.disconnect();
|
|
25
|
+
}, [src, lazy, isLoaded]);`,
|
|
26
|
+
getImageClassName: shared.getImageClassNameTemplate,
|
|
27
|
+
classNames: ` const imageClassName = getImageClassName(src);
|
|
28
|
+
const wrapperClassName = \`${imagePrefix}wrapper \${imageClassName} \${className || ''}\`.trim();
|
|
29
|
+
|
|
30
|
+
// Build img className with lazy states
|
|
31
|
+
const lazyClass = lazy ? (isLoaded ? '${constants_1.CLASS_NAMES.LAZY} ${constants_1.CLASS_NAMES.LOADED}' : '${constants_1.CLASS_NAMES.LAZY}') : '';
|
|
32
|
+
const imgClassName = \`${constants_1.CLASS_NAMES.IMAGE_BLOCK} \${lazyClass}\`.trim();`,
|
|
33
|
+
jsx: ` return (
|
|
34
|
+
<picture className={wrapperClassName}>
|
|
35
|
+
<img
|
|
36
|
+
ref={imgRef}
|
|
37
|
+
src={imageSrc}
|
|
38
|
+
alt={alt}
|
|
39
|
+
className={imgClassName}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
</picture>
|
|
43
|
+
);`,
|
|
44
|
+
export: `export default PlaceBlockImage;`
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
exports.getSharedReactTemplate = getSharedReactTemplate;
|
|
48
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../../../src/templates/shared/react.ts"],"names":[],"mappings":";;;AAAA,mCAA4C;AAC5C,+CAA8C;AAEvC,MAAM,sBAAsB,GAAG,CAAC,WAAmB,EAAE,EAAE;IAC5D,MAAM,MAAM,GAAG,IAAA,yBAAiB,EAAC,WAAW,CAAC,CAAC;IAE9C,OAAO;QACL,OAAO,EAAE,6DAA6D;QAEtE,OAAO,EAAE,MAAM,CAAC,OAAO;QAEvB,KAAK,EAAE;;;;;;;MAOL,MAAM,CAAC,4BAA4B;;;;;;;6BAOZ;QAEzB,iBAAiB,EAAE,MAAM,CAAC,yBAAyB;QAEnD,UAAU,EAAE;+BACe,WAAW;;;0CAGA,uBAAW,CAAC,IAAI,IAAI,uBAAW,CAAC,MAAM,QAAQ,uBAAW,CAAC,IAAI;2BAC7E,uBAAW,CAAC,WAAW,0BAA0B;QAExE,GAAG,EAAE;;;;;;;;;;KAUJ;QAED,MAAM,EAAE,iCAAiC;KAC1C,CAAC;AACJ,CAAC,CAAC;AA/CW,QAAA,sBAAsB,0BA+CjC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.d.ts","sourceRoot":"","sources":["../../src/templates/vue.ts"],"names":[],"mappings":"AAGA,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA8F1D"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getVueTemplate = getVueTemplate;
|
|
4
|
+
const shared_1 = require("./shared/");
|
|
5
|
+
const constants_1 = require("../constants");
|
|
6
|
+
function getVueTemplate(imagePrefix) {
|
|
7
|
+
const shared = (0, shared_1.getSharedTemplate)(imagePrefix);
|
|
8
|
+
return `<template>
|
|
9
|
+
<picture :class="wrapperClassName">
|
|
10
|
+
<img
|
|
11
|
+
ref="imgRef"
|
|
12
|
+
:src="imageSrc"
|
|
13
|
+
:alt="alt"
|
|
14
|
+
:class="imgClassName"
|
|
15
|
+
v-bind="$attrs"
|
|
16
|
+
/>
|
|
17
|
+
</picture>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup lang="ts">
|
|
21
|
+
import { computed, ref, onMounted, onUnmounted, watch } from 'vue';
|
|
22
|
+
|
|
23
|
+
interface Props {
|
|
24
|
+
src: string;
|
|
25
|
+
alt: string;
|
|
26
|
+
lazy?: boolean;
|
|
27
|
+
class?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
31
|
+
lazy: false,
|
|
32
|
+
class: ''
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
${shared.comment}
|
|
36
|
+
|
|
37
|
+
const imgRef = ref<HTMLImageElement | null>(null);
|
|
38
|
+
const imageSrc = ref(props.lazy ? '' : props.src);
|
|
39
|
+
const isLoaded = ref(!props.lazy);
|
|
40
|
+
let observer: IntersectionObserver | null = null;
|
|
41
|
+
|
|
42
|
+
${shared.getImageClassNameTemplate}
|
|
43
|
+
|
|
44
|
+
const imageClassName = computed(() => getImageClassName(props.src));
|
|
45
|
+
const wrapperClassName = computed(() =>
|
|
46
|
+
\`${imagePrefix}wrapper \${imageClassName.value}\`
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const lazyClass = computed(() =>
|
|
50
|
+
props.lazy ? (isLoaded.value ? '${constants_1.CLASS_NAMES.LAZY} ${constants_1.CLASS_NAMES.LOADED}' : '${constants_1.CLASS_NAMES.LAZY}') : ''
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const imgClassName = computed(() =>
|
|
54
|
+
\`\${props.class} \${lazyClass.value}\`.trim()
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const setupLazyLoading = () => {
|
|
58
|
+
if (!props.lazy || isLoaded.value) return;
|
|
59
|
+
|
|
60
|
+
${shared.intersectionObserverTemplate}
|
|
61
|
+
|
|
62
|
+
if (imgRef.value) {
|
|
63
|
+
observer.observe(imgRef.value);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const cleanupObserver = () => {
|
|
68
|
+
if (observer) {
|
|
69
|
+
observer.disconnect();
|
|
70
|
+
observer = null;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
onMounted(() => {
|
|
75
|
+
setupLazyLoading();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
onUnmounted(() => {
|
|
79
|
+
cleanupObserver();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
watch(() => props.src, (newSrc) => {
|
|
83
|
+
if (!props.lazy) {
|
|
84
|
+
imageSrc.value = newSrc;
|
|
85
|
+
} else if (!isLoaded.value) {
|
|
86
|
+
cleanupObserver();
|
|
87
|
+
setupLazyLoading();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
watch(isLoaded, (loaded) => {
|
|
92
|
+
if (loaded) {
|
|
93
|
+
imageSrc.value = props.src;
|
|
94
|
+
cleanupObserver();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
</script>
|
|
98
|
+
`;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.js","sourceRoot":"","sources":["../../src/templates/vue.ts"],"names":[],"mappings":";;AAGA,wCA8FC;AAjGD,sCAA8C;AAC9C,4CAA2C;AAE3C,SAAgB,cAAc,CAAC,WAAmB;IAChD,MAAM,MAAM,GAAG,IAAA,0BAAiB,EAAC,WAAW,CAAC,CAAC;IAE9C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BP,MAAM,CAAC,OAAO;;;;;;;EAOd,MAAM,CAAC,yBAAyB;;;;MAI5B,WAAW;;;;oCAImB,uBAAW,CAAC,IAAI,IAAI,uBAAW,CAAC,MAAM,QAAQ,uBAAW,CAAC,IAAI;;;;;;;;;;IAU9F,MAAM,CAAC,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsCtC,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function getReactTsxTemplate(imagePrefix: string): string;
|
|
2
|
+
export declare function getReactJsxTemplate(imagePrefix: string): string;
|
|
3
|
+
export declare function getVueTemplate(imagePrefix: string): string;
|
|
4
|
+
export declare function getTemplate(type: 'tsx' | 'jsx' | 'vue', imagePrefix: string): string;
|
|
5
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAKA,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAWpF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getReactTsxTemplate = getReactTsxTemplate;
|
|
4
|
+
exports.getReactJsxTemplate = getReactJsxTemplate;
|
|
5
|
+
exports.getVueTemplate = getVueTemplate;
|
|
6
|
+
exports.getTemplate = getTemplate;
|
|
7
|
+
// Main template exports
|
|
8
|
+
const react_tsx_1 = require("./templates/react-tsx");
|
|
9
|
+
const react_jsx_1 = require("./templates/react-jsx");
|
|
10
|
+
const vue_1 = require("./templates/vue");
|
|
11
|
+
function getReactTsxTemplate(imagePrefix) {
|
|
12
|
+
return (0, react_tsx_1.getReactTsxTemplate)(imagePrefix);
|
|
13
|
+
}
|
|
14
|
+
function getReactJsxTemplate(imagePrefix) {
|
|
15
|
+
return (0, react_jsx_1.getReactJsxTemplate)(imagePrefix);
|
|
16
|
+
}
|
|
17
|
+
function getVueTemplate(imagePrefix) {
|
|
18
|
+
return (0, vue_1.getVueTemplate)(imagePrefix);
|
|
19
|
+
}
|
|
20
|
+
function getTemplate(type, imagePrefix) {
|
|
21
|
+
switch (type) {
|
|
22
|
+
case 'tsx':
|
|
23
|
+
return getReactTsxTemplate(imagePrefix);
|
|
24
|
+
case 'jsx':
|
|
25
|
+
return getReactJsxTemplate(imagePrefix);
|
|
26
|
+
case 'vue':
|
|
27
|
+
return getVueTemplate(imagePrefix);
|
|
28
|
+
default:
|
|
29
|
+
throw new Error(`Unsupported component type: ${type}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":";;AAKA,kDAEC;AAED,kDAEC;AAED,wCAEC;AAED,kCAWC;AA5BD,wBAAwB;AACxB,qDAA2E;AAC3E,qDAA2E;AAC3E,yCAA2D;AAE3D,SAAgB,mBAAmB,CAAC,WAAmB;IACrD,OAAO,IAAA,+BAAW,EAAC,WAAW,CAAC,CAAC;AAClC,CAAC;AAED,SAAgB,mBAAmB,CAAC,WAAmB;IACrD,OAAO,IAAA,+BAAW,EAAC,WAAW,CAAC,CAAC;AAClC,CAAC;AAED,SAAgB,cAAc,CAAC,WAAmB;IAChD,OAAO,IAAA,oBAAM,EAAC,WAAW,CAAC,CAAC;AAC7B,CAAC;AAED,SAAgB,WAAW,CAAC,IAA2B,EAAE,WAAmB;IAC1E,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,KAAK;YACR,OAAO,mBAAmB,CAAC,WAAW,CAAC,CAAC;QAC1C,KAAK,KAAK;YACR,OAAO,mBAAmB,CAAC,WAAW,CAAC,CAAC;QAC1C,KAAK,KAAK;YACR,OAAO,cAAc,CAAC,WAAW,CAAC,CAAC;QACrC;YACE,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface TemplateData {
|
|
2
|
+
imagePrefix: string;
|
|
3
|
+
baseClassName: string;
|
|
4
|
+
wrapperClassName: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function getTemplateData(imagePrefix: string): TemplateData;
|
|
7
|
+
export declare function getSharedLogic(imagePrefix: string): string;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CAMjE;AAED,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAkB1D"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Shared template utilities
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.getTemplateData = getTemplateData;
|
|
5
|
+
exports.getSharedLogic = getSharedLogic;
|
|
6
|
+
function getTemplateData(imagePrefix) {
|
|
7
|
+
return {
|
|
8
|
+
imagePrefix,
|
|
9
|
+
baseClassName: `${imagePrefix}block`,
|
|
10
|
+
wrapperClassName: `${imagePrefix}wrapper`
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function getSharedLogic(imagePrefix) {
|
|
14
|
+
return `
|
|
15
|
+
// Extract filename from src to generate class name
|
|
16
|
+
const getImageClassName = (imageSrc) => {
|
|
17
|
+
// Remove /images/ prefix and file extension, convert to kebab-case
|
|
18
|
+
const filename = imageSrc
|
|
19
|
+
.replace(/^.*\\/images\\//, '') // Remove path up to /images/
|
|
20
|
+
.replace(/\\.[^/.]+$/, '') // Remove file extension
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.replace(/[^a-z0-9-]/g, '-') // Convert special chars to hyphens
|
|
23
|
+
.replace(/-+/g, '-') // Remove duplicate hyphens
|
|
24
|
+
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
|
25
|
+
|
|
26
|
+
return \`${imagePrefix}\${filename}\`;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const imageClassName = getImageClassName(src);
|
|
30
|
+
const wrapperClassName = \`${imagePrefix}wrapper \${imageClassName}\`;`;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";AAAA,4BAA4B;;AAQ5B,0CAMC;AAED,wCAkBC;AA1BD,SAAgB,eAAe,CAAC,WAAmB;IACjD,OAAO;QACL,WAAW;QACX,aAAa,EAAE,GAAG,WAAW,OAAO;QACpC,gBAAgB,EAAE,GAAG,WAAW,SAAS;KAC1C,CAAC;AACJ,CAAC;AAED,SAAgB,cAAc,CAAC,WAAmB;IAChD,OAAO;;;;;;;;;;;;eAYM,WAAW;;;;+BAIK,WAAW,+BAA+B,CAAC;AAC1E,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Compiler } from 'webpack';
|
|
2
|
+
export interface PlaceBlockImagePluginOptions {
|
|
3
|
+
imagePrefix?: string;
|
|
4
|
+
imageDir: string;
|
|
5
|
+
scssPath: string;
|
|
6
|
+
componentPath?: string;
|
|
7
|
+
componentType?: 'tsx' | 'jsx' | 'vue';
|
|
8
|
+
generateComponent?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface ImageDimensions {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
filename: string;
|
|
14
|
+
className: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class PlaceBlockImagePlugin {
|
|
17
|
+
private options;
|
|
18
|
+
private isGenerating;
|
|
19
|
+
private lastGenerationTime;
|
|
20
|
+
constructor(options: PlaceBlockImagePluginOptions);
|
|
21
|
+
/**
|
|
22
|
+
* Generate CSS class name from filename
|
|
23
|
+
*/
|
|
24
|
+
private generateClassName;
|
|
25
|
+
/**
|
|
26
|
+
* Scan directory for images and get their dimensions
|
|
27
|
+
*/
|
|
28
|
+
private scanImages;
|
|
29
|
+
/**
|
|
30
|
+
* Generate SCSS with CSS custom properties and base class
|
|
31
|
+
*/
|
|
32
|
+
private generateScss;
|
|
33
|
+
/**
|
|
34
|
+
* Write SCSS file
|
|
35
|
+
*/
|
|
36
|
+
private writeScssFile;
|
|
37
|
+
/**
|
|
38
|
+
* Generate and write component file
|
|
39
|
+
*/
|
|
40
|
+
private writeComponentFile;
|
|
41
|
+
/**
|
|
42
|
+
* Check if images have changed since last generation
|
|
43
|
+
*/
|
|
44
|
+
private shouldRegenerate;
|
|
45
|
+
apply(compiler: Compiler): void;
|
|
46
|
+
private generateImageStyles;
|
|
47
|
+
}
|
|
48
|
+
export default PlaceBlockImagePlugin;
|
|
49
|
+
//# sourceMappingURL=webpack-plugin.d.ts.map
|