Skip to content

Commit 228a233

Browse files
committed
feat: add manual inject styles function into the project
1 parent d3f2b34 commit 228a233

File tree

5 files changed

+130
-16
lines changed

5 files changed

+130
-16
lines changed

.npmignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,18 @@ coverage/
2525
.vscode/
2626
.husky/
2727
.github/
28+
rollup-plugins/
2829

2930
// misc files
3031
bundlesize.config.json
32+
prebuild.js
33+
jest.config.ts
3134

3235
// bundler - rollup
3336
rollup.config.dev.js
3437
rollup.config.prod.js
3538
rollup.config.types.js
39+
40+
// bundler - esbuild
41+
esbuild.config.dev.mjs
42+
esbuild.config.prod.mjs
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* eslint-disable no-await-in-loop */
2+
import { readFile, writeFile } from 'node:fs/promises'
3+
4+
const cssMinifier = (css) =>
5+
css
6+
.replace(/([^0-9a-zA-Z.#])\s+/g, '$1')
7+
.replace(/\s([^0-9a-zA-Z.#]+)/g, '$1')
8+
.replace(/;}/g, '}')
9+
.replace(/\/\*.*?\*\//g, '')
10+
11+
/* eslint-disable no-param-reassign */
12+
/**
13+
* This plugin is very similar to the `replace`, but instead of only
14+
* check for strings, this plugin check for everything that matches the query.
15+
* expected input: { 'lorem:': 'ipsum' }
16+
* or
17+
* expected input: { 'lorem:': 'file:myfile.css' }
18+
*/
19+
export default function replaceBeforeSaveFile(replaceObject = {}) {
20+
return {
21+
// this name will show up in warnings and errors of rollup execution
22+
name: 'modify-generated-files',
23+
/**
24+
* This write bundle is executed after the files being written.
25+
* Docs: https://rollupjs.org/plugin-development/#writebundle
26+
*/
27+
async writeBundle(options, bundle) {
28+
const replaceKeys = Object.keys(replaceObject)
29+
if (replaceKeys.length > 0) {
30+
const files = Object.keys(bundle)
31+
32+
let file = null
33+
let key = null
34+
let regex = null
35+
for (let index = 0; index < files.length; index += 1) {
36+
file = files[index]
37+
38+
if (file && bundle[file].code) {
39+
for (let indexKeys = 0; indexKeys < replaceKeys.length; indexKeys += 1) {
40+
key = replaceKeys[indexKeys]
41+
regex = new RegExp(key, 'g')
42+
43+
if (bundle[file].code.includes(key)) {
44+
if (replaceObject[key].includes('file:')) {
45+
const [, fileName] = replaceObject[key].split(':')
46+
const fileContent = await readFile(`./dist/${fileName}`, 'utf8')
47+
48+
if (options.file.includes('.min')) {
49+
bundle[file].code = bundle[file].code.replace(
50+
regex,
51+
`\`${cssMinifier(fileContent)}\``,
52+
)
53+
} else {
54+
bundle[file].code = bundle[file].code.replace(regex, `\`${fileContent}\``)
55+
}
56+
} else {
57+
bundle[file].code = bundle[file].code.replace(regex, replaceObject[key])
58+
}
59+
60+
await writeFile(options.file, bundle[file].code)
61+
}
62+
}
63+
}
64+
}
65+
}
66+
},
67+
}
68+
}

rollup.config.prod.js

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { nodeResolve } from '@rollup/plugin-node-resolve'
77
import ts from '@rollup/plugin-typescript'
88
import { terser } from 'rollup-plugin-terser'
99
import typescript from 'typescript'
10+
import { fileURLToPath } from 'node:url'
11+
import replaceBeforeSaveFile from './rollup-plugins/replace-before-save-file.js'
1012
import * as pkg from './package.json'
1113

1214
const input = ['src/index.tsx']
@@ -24,21 +26,13 @@ const banner = `
2426
const external = [
2527
...Object.keys(pkg.peerDependencies ?? {}),
2628
...Object.keys(pkg.dependencies ?? {}),
29+
fileURLToPath(new URL('temp-path-for-styles', import.meta.url)),
2730
]
2831

2932
const buildFormats = [
30-
/**
31-
* Temporary build to keep the extracted CSS file.
32-
* I don't want to do a major release now with only the CSS change,
33-
* so, we will keep the css being exported by the lib and now
34-
* we will inject the css into the head by default.
35-
* The CSS file import is deprecated and the file is only
36-
* for style reference now.
37-
*/
3833
{
3934
file: 'dist/react-tooltip.mjs',
4035
format: 'es',
41-
extractCSS: true,
4236
},
4337
{
4438
file: 'dist/react-tooltip.umd.js',
@@ -74,37 +68,39 @@ const sharedPlugins = [
7468
typescript,
7569
tsconfig: './tsconfig.json',
7670
noEmitOnError: false,
77-
// declaration: true,
78-
// declarationDir: './build',
7971
}),
8072
commonjs({
8173
include: 'node_modules/**',
8274
}),
8375
]
8476
// this step is just to build the minified javascript files
85-
const minifiedBuildFormats = buildFormats.map(({ file, extractCSS, ...rest }) => ({
77+
const minifiedBuildFormats = buildFormats.map(({ file, ...rest }) => ({
8678
file: file.replace(/(\.[cm]?js)$/, '.min$1'),
8779
...rest,
8880
minify: true,
89-
extractCSS,
9081
plugins: [terser({ compress: { directives: false } }), filesize()],
9182
}))
9283

9384
const allBuildFormats = [...buildFormats, ...minifiedBuildFormats]
9485

9586
const config = allBuildFormats.map(
96-
({ file, format, globals, plugins: specificPlugins, minify, extractCSS }) => {
87+
({ file, format, globals, plugins: specificPlugins, minify }) => {
9788
const plugins = [
9889
...sharedPlugins,
9990
postcss({
100-
// eslint-disable-next-line no-nested-ternary
101-
extract: extractCSS ? (minify ? 'react-tooltip.min.css' : 'react-tooltip.css') : false, // this will generate a specific file and override on multiples build, but the css will be the same
91+
extract: minify ? 'react-tooltip.min.css' : 'react-tooltip.css', // this will generate a specific file and override on multiples build, but the css will be the same
10292
autoModules: true,
10393
include: '**/*.css',
10494
extensions: ['.css'],
10595
plugins: [],
10696
minimize: Boolean(minify),
10797
}),
98+
replaceBeforeSaveFile({
99+
// this only works for the react-tooltip.css because it's the first file
100+
// writen in our build process before the javascript files.
101+
"'temp-content-for-styles'": 'file:react-tooltip.css',
102+
'"temp-content-for-styles"': 'file:react-tooltip.css',
103+
}),
108104
]
109105

110106
if (specificPlugins && specificPlugins.length) {

src/index.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import './tokens.css'
2+
3+
import styleInject from 'utils/style-inject'
4+
25
import type {
36
ChildrenType,
47
DataAttribute,
@@ -13,6 +16,12 @@ import type {
1316
import type { ITooltipController } from './components/TooltipController/TooltipControllerTypes'
1417
import type { ITooltipWrapper } from './components/TooltipProvider/TooltipProviderTypes'
1518

19+
// this content will be replaced in build time with the `react-tooltip.css` builded content
20+
const TooltipStyles = 'temp-content-for-styles'
21+
22+
// @ts-ignore
23+
styleInject(TooltipStyles)
24+
1625
export { TooltipController as Tooltip } from './components/TooltipController'
1726
export { TooltipProvider, TooltipWrapper } from './components/TooltipProvider'
1827
export type {

src/utils/style-inject.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
function styleInject(css: string, ref?: any) {
2+
if (!ref) {
3+
// eslint-disable-next-line no-param-reassign
4+
ref = {}
5+
}
6+
const { insertAt } = ref
7+
8+
if (!css || typeof document === 'undefined' || document.getElementById('react-tooltip-styles')) {
9+
return
10+
}
11+
12+
const head = document.head || document.getElementsByTagName('head')[0]
13+
const style: any = document.createElement('style')
14+
style.id = 'react-tooltip-styles'
15+
style.type = 'text/css'
16+
17+
if (insertAt === 'top') {
18+
if (head.firstChild) {
19+
head.insertBefore(style, head.firstChild)
20+
} else {
21+
head.appendChild(style)
22+
}
23+
} else {
24+
head.appendChild(style)
25+
}
26+
27+
if (style.styleSheet) {
28+
style.styleSheet.cssText = css
29+
} else {
30+
style.appendChild(document.createTextNode(css))
31+
}
32+
}
33+
34+
export default styleInject

0 commit comments

Comments
 (0)