vite-plugin-css-position 2.0.2

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 ADDED
@@ -0,0 +1,124 @@
1
+ # vite-plugin-react-css-position
2
+
3
+ [![npm version](https://img.shields.io/npm/v/vite-plugin-react-css-position)](https://www.npmjs.com/package/vite-plugin-react-css-position)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ A Vite plugin that allows you to control where CSS stylesheets are injected in your React application. Perfect for scenarios where you need precise control over style placement, especially when working with Shadow DOM.
7
+
8
+ ## ✨ Features
9
+
10
+ - 🎯 **Custom CSS positioning** - Place stylesheets exactly where you need them in your component tree
11
+ - 🌗 **Shadow DOM support** - Ideal for Shadow DOM implementations where styles need to be scoped
12
+ - 🚀 **Development mode** - Optional hot module replacement support
13
+
14
+ ## 📦 Installation
15
+
16
+ ```bash
17
+ npm install vite-plugin-react-css-position
18
+ ```
19
+
20
+ or
21
+
22
+ ```bash
23
+ pnpm add vite-plugin-react-css-position
24
+ ```
25
+
26
+ or
27
+
28
+ ```bash
29
+ yarn add vite-plugin-react-css-position
30
+ ```
31
+
32
+ ## 🚀 Quick Start
33
+
34
+ ### 1. Configure Vite
35
+
36
+ Add the plugin to your `vite.config.ts`:
37
+
38
+ ```typescript
39
+ ...
40
+ import { viteReactCssPosition } from "vite-plugin-react-css-position";
41
+
42
+ export default defineConfig({
43
+ plugins: [react(), /* or vue(), */ viteReactCssPosition()],
44
+ });
45
+ ```
46
+
47
+ ### 2. Use StylesTarget Component
48
+
49
+ Import and place the `StylesTarget` component where you want your styles to be injected:
50
+
51
+ #### In React
52
+
53
+ ```tsx
54
+ import StylesTarget from "vite-plugin-react-css-position/react";
55
+
56
+ export function App() {
57
+ return (
58
+ <div>
59
+ <StylesTarget />
60
+ <span>Your App Content</span>
61
+ </div>
62
+ );
63
+ }
64
+ ```
65
+
66
+ #### In Vue
67
+
68
+ ```vue
69
+ <script setup lang="ts">
70
+ import StylesTarget from "vite-plugin-css-position/vue";
71
+ </script>
72
+
73
+ <template>
74
+ <div>
75
+ <StylesTarget />
76
+ </div>
77
+ </template>
78
+ ```
79
+
80
+ That's it! Your stylesheets will now be injected at the position of the `<StylesTarget />` component.
81
+
82
+ ## ⚙️ Configuration
83
+
84
+ The plugin accepts optional configuration:
85
+
86
+ ```typescript
87
+ viteReactCssPosition({
88
+ enableDev: true,
89
+ });
90
+ ```
91
+
92
+ ### Options
93
+
94
+ - **`instanceId`** - A custom identifier for the plugin instance. Useful when you have multiple instances or need to avoid conflicts. Defaults to a random UUID.
95
+ - **`enableDev`** - When `true`, enables CSS injection during development mode. Defaults to `false`. Enable this for HMR support
96
+
97
+ ## 🛠️ Development
98
+
99
+ ```bash
100
+ # Install dependencies
101
+ npm install
102
+
103
+ # Run the playground
104
+ npm run play
105
+
106
+ # Build the library
107
+ npm run build
108
+
109
+ # Run tests
110
+ npm test
111
+
112
+ # Type check
113
+ npm run typecheck
114
+ ```
115
+
116
+ ## 📝 License
117
+
118
+ MIT © [Alexander Bogoslawski](https://github.com/Ahn1)
119
+
120
+ ## 🔗 Links
121
+
122
+ - [GitHub Repository](https://github.com/Ahn1/vite-plugin-react-css-position)
123
+ - [Issue Tracker](https://github.com/Ahn1/vite-plugin-react-css-position/issues)
124
+ - [npm Package](https://www.npmjs.com/package/vite-plugin-react-css-position)
@@ -0,0 +1,6 @@
1
+ export interface StylesTargetProps {
2
+ onChange?: (stylesMap: Map<string, {
3
+ css: string;
4
+ attributes: Record<string, string>;
5
+ }>) => void;
6
+ }
@@ -0,0 +1,15 @@
1
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
2
+
3
+ //#region src/StylesTarget.types.d.ts
4
+ interface StylesTargetProps {
5
+ onChange?: (stylesMap: Map<string, {
6
+ css: string;
7
+ attributes: Record<string, string>;
8
+ }>) => void;
9
+ }
10
+ //#endregion
11
+ //#region src/StylesTargetReact.d.ts
12
+ declare const StylesTarget: (props: StylesTargetProps) => (react_jsx_runtime0.JSX.Element | null)[];
13
+ //#endregion
14
+ export { StylesTarget as default };
15
+ //# sourceMappingURL=StylesTargetReact.d.ts.map
@@ -0,0 +1,2 @@
1
+ import{createElement as e,useEffect as t,useState as n}from"react";const r=__VITE_CSS_POS_GLOBAL_VAR_NAME__,i=__VITE_CSS_POS_EVENT_NAME__,a=()=>window[r];var o=r=>{let[o,s]=n(a()||new Map),[c,l]=n(0);return t(()=>{let e=e=>{let t=a()||new Map;s(t),l(e=>e+1),r.onChange?.(t)};return window.addEventListener(i,e),e(void 0),()=>{window.removeEventListener(i,e)}},[r.onChange]),Array.from(o?.keys()||[]).map(t=>{let n=o.get(t);return n?e(`style`,{...n.attributes,key:`${t}-${c}`},n.css):null})};export{o as default};
2
+ //# sourceMappingURL=StylesTargetReact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StylesTargetReact.js","names":[],"sources":["../src/StylesTargetReact.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { StylesTargetProps } from \"./StylesTarget.types\";\n\ndeclare const __VITE_CSS_POS_GLOBAL_VAR_NAME__: string;\ndeclare const __VITE_CSS_POS_EVENT_NAME__: string;\n\nconst globalVarName = __VITE_CSS_POS_GLOBAL_VAR_NAME__;\nconst eventName = __VITE_CSS_POS_EVENT_NAME__;\n\nconst getCurrent = () => (window as any)[globalVarName];\n\nconst StylesTarget = (props: StylesTargetProps) => {\n const [stylesMap, setStylesMap] = useState<\n Map<string, { css: string; attributes: Record<string, string> }>\n >(getCurrent() || new Map());\n\n const [version, setVersion] = useState(0);\n\n useEffect(() => {\n const updateListener = (_e: Event | undefined) => {\n const newValues = getCurrent() || new Map();\n setStylesMap(newValues);\n setVersion((v) => v + 1);\n props.onChange?.(newValues);\n };\n window.addEventListener(eventName, updateListener);\n\n updateListener(undefined);\n\n return () => {\n window.removeEventListener(eventName, updateListener);\n };\n }, [props.onChange]);\n\n return Array.from(stylesMap?.keys() || []).map((key) => {\n const entry = stylesMap.get(key);\n if (!entry) return null;\n return (\n <style {...entry.attributes} key={`${key}-${version}`}>\n {entry.css}\n </style>\n );\n });\n};\n\nexport default StylesTarget;\n"],"mappings":"mEAMA,MAAM,EAAgB,iCAChB,EAAY,4BAEZ,MAAoB,OAAe,GAoCzC,IAAA,EAlCsB,GAA6B,CACjD,GAAM,CAAC,EAAW,GAAgB,EAEhC,GAAY,EAAI,IAAI,IAAM,CAEtB,CAAC,EAAS,GAAc,EAAS,EAAE,CAkBzC,OAhBA,MAAgB,CACd,IAAM,EAAkB,GAA0B,CAChD,IAAM,EAAY,GAAY,EAAI,IAAI,IACtC,EAAa,EAAU,CACvB,EAAY,GAAM,EAAI,EAAE,CACxB,EAAM,WAAW,EAAU,EAM7B,OAJA,OAAO,iBAAiB,EAAW,EAAe,CAElD,EAAe,IAAA,GAAU,KAEZ,CACX,OAAO,oBAAoB,EAAW,EAAe,GAEtD,CAAC,EAAM,SAAS,CAAC,CAEb,MAAM,KAAK,GAAW,MAAM,EAAI,EAAE,CAAC,CAAC,IAAK,GAAQ,CACtD,IAAM,EAAQ,EAAU,IAAI,EAAI,CAEhC,OADK,EAEH,EAAC,QAAA,CAAM,GAAI,EAAM,WAAY,IAAK,GAAG,EAAI,GAAG,KACzC,EAAM,IACD,CAJS,MAMnB"}
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import { defineComponent, ref, h, onMounted, onUnmounted } from "vue";
3
+ import type { StylesTargetProps } from "./StylesTarget.types";
4
+
5
+ declare const __VITE_CSS_POS_GLOBAL_VAR_NAME__: string;
6
+ declare const __VITE_CSS_POS_EVENT_NAME__: string;
7
+
8
+ const globalVarName = __VITE_CSS_POS_GLOBAL_VAR_NAME__;
9
+ const eventName = __VITE_CSS_POS_EVENT_NAME__;
10
+
11
+ const getCurrent = () => (window as any)[globalVarName];
12
+
13
+ export default defineComponent({
14
+ name: "StylesTarget",
15
+ props: {
16
+ onChange: {
17
+ type: Function as unknown as () => StylesTargetProps["onChange"],
18
+ required: false,
19
+ },
20
+ },
21
+ setup(props) {
22
+ const stylesMap = ref<Map<string, { css: string; attributes: Record<string, string> }>>(
23
+ getCurrent() || new Map()
24
+ );
25
+ const version = ref(0);
26
+
27
+ const updateListener = (_e: Event | undefined) => {
28
+ const newValues = getCurrent() || new Map();
29
+ stylesMap.value = newValues;
30
+ version.value++;
31
+ (props.onChange as StylesTargetProps["onChange"])?.(newValues);
32
+ };
33
+
34
+ onMounted(() => {
35
+ window.addEventListener(eventName, updateListener);
36
+ updateListener(undefined);
37
+ });
38
+
39
+ onUnmounted(() => {
40
+ window.removeEventListener(eventName, updateListener);
41
+ });
42
+
43
+ return () => {
44
+ return Array.from(stylesMap.value?.keys() || []).map((key) => {
45
+ const entry = stylesMap.value.get(key);
46
+ if (!entry) return null;
47
+ return h("style", { key: `${key}-${version.value}`, ...entry.attributes }, entry.css);
48
+ });
49
+ };
50
+ },
51
+ });
52
+ </script>
@@ -0,0 +1,16 @@
1
+ import type { StylesTargetProps } from "./StylesTarget.types";
2
+ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
3
+ onChange: {
4
+ type: () => StylesTargetProps["onChange"];
5
+ required: false;
6
+ };
7
+ }>, () => (import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
8
+ [key: string]: any;
9
+ }> | null)[], {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
10
+ onChange: {
11
+ type: () => StylesTargetProps["onChange"];
12
+ required: false;
13
+ };
14
+ }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
15
+ declare const _default: typeof __VLS_export;
16
+ export default _default;
@@ -0,0 +1,11 @@
1
+ import { Plugin } from "vite";
2
+
3
+ //#region src/viteCustomCssPosition.d.ts
4
+ interface ViteCustomCssPositionOptions {
5
+ instanceId?: string;
6
+ enableDev?: boolean;
7
+ }
8
+ declare function viteCustomCssPosition(options?: ViteCustomCssPositionOptions): Plugin | Plugin[];
9
+ //#endregion
10
+ export { type ViteCustomCssPositionOptions, viteCustomCssPosition as viteReactCssPosition };
11
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ import e from"vite-plugin-css-injected-by-js";import{hash as t,randomUUID as n}from"crypto";function r(r){let i=r?.instanceId||n().replace(/-/g,``),a=`__vite_c_css_pos_initial_${i}`,o=`__vite_c_css_pos_update_${i}`,s=e({dev:{enableDev:r?.enableDev??!1,removeStyleCode(e){return`
2
+ (() => {
3
+ if(window.${a} && window.${a}.has('${e}')) {
4
+ window.${a}.delete('${e}');
5
+ window.dispatchEvent( new Event('${o}') );
6
+ }
7
+ })()
8
+ `}},injectCode:(e,n)=>{let r=JSON.stringify(n.attributes||{});return`
9
+ const css = ${e};
10
+ const id = ${`"${n.attributes?.[`data-vite-dev-id`]??t(`sha1`,e).substring(0,12)}"`};
11
+ const attributes = JSON.parse('${r}');
12
+ window.${a} = window.${a} || new Map();
13
+ window.${a}.set(id, {css, attributes});
14
+
15
+ window.dispatchEvent( new Event('${o}') );
16
+ `}});return[{name:`vite-plugin-custom-css-position`,config(e){return{...e,define:{...e.define,__VITE_CSS_POS_GLOBAL_VAR_NAME__:JSON.stringify(a),__VITE_CSS_POS_EVENT_NAME__:JSON.stringify(o)}}}},...Array.isArray(s)?s:[s]]}export{r as viteReactCssPosition};
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/viteCustomCssPosition.ts"],"sourcesContent":["import type { Plugin } from \"vite\";\nimport cssInjectedByJsPlugin from \"vite-plugin-css-injected-by-js\";\nimport { randomUUID, hash } from \"crypto\";\n\nexport interface ViteCustomCssPositionOptions {\n instanceId?: string;\n enableDev?: boolean;\n}\n\nexport default function viteCustomCssPosition(\n options?: ViteCustomCssPositionOptions\n): Plugin | Plugin[] {\n const instanceId = options?.instanceId || randomUUID().replace(/-/g, \"\");\n\n const globalVarName = `__vite_c_css_pos_initial_${instanceId}`;\n const eventName = `__vite_c_css_pos_update_${instanceId}`;\n\n const cssPlugin = cssInjectedByJsPlugin({\n dev: {\n enableDev: options?.enableDev ?? false,\n removeStyleCode(id: string) {\n return `\n (() => {\n if(window.${globalVarName} && window.${globalVarName}.has('${id}')) {\n window.${globalVarName}.delete('${id}');\n window.dispatchEvent( new Event('${eventName}') );\n }\n })()\n `;\n },\n },\n injectCode: (css, attributes) => {\n const attributesString = JSON.stringify(attributes.attributes || {});\n const id = `\"${\n attributes.attributes?.[\"data-vite-dev-id\"] ??\n hash(\"sha1\", css).substring(0, 12)\n }\"`;\n return `\n const css = ${css};\n const id = ${id};\n const attributes = JSON.parse('${attributesString}');\n window.${globalVarName} = window.${globalVarName} || new Map();\n window.${globalVarName}.set(id, {css, attributes});\n\n window.dispatchEvent( new Event('${eventName}') );\n `;\n },\n });\n\n return [\n {\n name: \"vite-plugin-custom-css-position\",\n config(c) {\n return {\n ...c,\n define: {\n ...c.define,\n __VITE_CSS_POS_GLOBAL_VAR_NAME__: JSON.stringify(globalVarName),\n __VITE_CSS_POS_EVENT_NAME__: JSON.stringify(eventName),\n },\n };\n },\n },\n ...(Array.isArray(cssPlugin) ? cssPlugin : [cssPlugin]),\n ];\n}\n"],"mappings":"4FASA,SAAwB,EACtB,EACmB,CACnB,IAAM,EAAa,GAAS,YAAc,GAAY,CAAC,QAAQ,KAAM,GAAG,CAElE,EAAgB,4BAA4B,IAC5C,EAAY,2BAA2B,IAEvC,EAAY,EAAsB,CACtC,IAAK,CACH,UAAW,GAAS,WAAa,GACjC,gBAAgB,EAAY,CAC1B,MAAO;;sBAEO,EAAc,aAAa,EAAc,QAAQ,EAAG;qBACrD,EAAc,WAAW,EAAG;+CACF,EAAU;;;aAKpD,CACD,YAAa,EAAK,IAAe,CAC/B,IAAM,EAAmB,KAAK,UAAU,EAAW,YAAc,EAAE,CAAC,CAKpE,MAAO;kBACK,EAAI;iBALL,IACT,EAAW,aAAa,qBACxB,EAAK,OAAQ,EAAI,CAAC,UAAU,EAAG,GAAG,CACnC,GAGa;uCACmB,EAAiB;eACzC,EAAc,YAAY,EAAc;eACxC,EAAc;;yCAEY,EAAU;OAGhD,CAAC,CAEF,MAAO,CACL,CACE,KAAM,kCACN,OAAO,EAAG,CACR,MAAO,CACL,GAAG,EACH,OAAQ,CACN,GAAG,EAAE,OACL,iCAAkC,KAAK,UAAU,EAAc,CAC/D,4BAA6B,KAAK,UAAU,EAAU,CACvD,CACF,EAEJ,CACD,GAAI,MAAM,QAAQ,EAAU,CAAG,EAAY,CAAC,EAAU,CACvD"}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "vite-plugin-css-position",
3
+ "version": "2.0.2",
4
+ "description": "Custom position of vite styles within a vite react app",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/Ahn1/vite-plugin-react-css-position",
8
+ "bugs": {
9
+ "url": "https://github.com/Ahn1/vite-plugin-react-css-position/issues"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/Ahn1/vite-plugin-react-css-position.git"
14
+ },
15
+ "author": "Alexander Bogoslawski <alexanderb@posteo.de>",
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "main": "./dist/index.js",
20
+ "module": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "default": "./dist/index.js"
26
+ },
27
+ "./target": {
28
+ "types": "./dist/StylesTarget.d.ts",
29
+ "default": "./dist/StylesTarget.js"
30
+ },
31
+ "./react": {
32
+ "types": "./dist/StylesTarget.d.ts",
33
+ "default": "./dist/StylesTarget.js"
34
+ },
35
+ "./vue": {
36
+ "types": "./dist/StylesTargetVue.vue.d.ts",
37
+ "default": "./dist/StylesTargetVue.vue"
38
+ },
39
+ "./package.json": "./package.json"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "peerDependencies": {
45
+ "react": "^19.0.0",
46
+ "react-dom": "^19.0.0",
47
+ "vue": "^3.5.22"
48
+ },
49
+ "devDependencies": {
50
+ "@testing-library/jest-dom": "^6.9.1",
51
+ "@testing-library/react": "^16.3.0",
52
+ "@tsconfig/strictest": "^2.0.6",
53
+ "@types/node": "^24.9.1",
54
+ "@types/react": "^19.2.2",
55
+ "@types/react-dom": "^19.2.2",
56
+ "@vitejs/plugin-react": "^5.0.4",
57
+ "bumpp": "^10.3.1",
58
+ "globals": "^16.4.0",
59
+ "happy-dom": "^20.0.8",
60
+ "tsdown": "^0.15.9",
61
+ "typescript": "^5.9.3",
62
+ "typescript-eslint": "^8.45.0",
63
+ "unplugin-vue": "^7.0.4",
64
+ "vite": "^7.1.12",
65
+ "vitest": "^4.0.1",
66
+ "vue-tsc": "^3.1.3"
67
+ },
68
+ "dependencies": {
69
+ "vite-plugin-css-injected-by-js": "^3.5.2"
70
+ },
71
+ "scripts": {
72
+ "build": "tsdown && vue-tsc --project tsconfig.vue.json --skipLibCheck && cp src/StylesTargetVue.vue dist/StylesTargetVue.vue",
73
+ "dev": "tsdown --watch",
74
+ "play": "vite --config playground/vite.config.ts",
75
+ "test": "vitest",
76
+ "typecheck": "tsc --noEmit",
77
+ "release": "bumpp && pnpm publish"
78
+ }
79
+ }