feat: implement build system and development workflow
- Add npm scripts for CSS/JS compilation (build:css, build:js, build) - Create PowerShell build automation script - Document development workflow in README - Add troubleshooting guide for build issues - Specify proper file structure and compilation process Supports Tailwind CSS v3.4.0 and esbuild bundling with source maps.
This commit is contained in:
255
node_modules/tailwindcss/src/lib/defaultExtractor.js
generated
vendored
Normal file
255
node_modules/tailwindcss/src/lib/defaultExtractor.js
generated
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
import * as regex from './regex'
|
||||
import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly'
|
||||
|
||||
export function defaultExtractor(context) {
|
||||
let patterns = Array.from(buildRegExps(context))
|
||||
|
||||
/**
|
||||
* @param {string} content
|
||||
*/
|
||||
return (content) => {
|
||||
/** @type {(string|string)[]} */
|
||||
let results = []
|
||||
|
||||
for (let pattern of patterns) {
|
||||
for (let result of content.match(pattern) ?? []) {
|
||||
results.push(clipAtBalancedParens(result))
|
||||
}
|
||||
}
|
||||
|
||||
// Extract any subclasses from languages like Slim and Pug, eg:
|
||||
// div.flex.px-5.underline
|
||||
for (let result of results.slice()) {
|
||||
let segments = splitAtTopLevelOnly(result, '.')
|
||||
|
||||
for (let idx = 0; idx < segments.length; idx++) {
|
||||
let segment = segments[idx]
|
||||
if (idx >= segments.length - 1) {
|
||||
results.push(segment)
|
||||
continue
|
||||
}
|
||||
|
||||
// If the next segment is a number, discard both, for example seeing
|
||||
// `px-1` and `5` means the real candidate was `px-1.5` which is already
|
||||
// captured.
|
||||
let next = Number(segments[idx + 1])
|
||||
if (isNaN(next)) {
|
||||
results.push(segment)
|
||||
} else {
|
||||
idx++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
}
|
||||
|
||||
function* buildRegExps(context) {
|
||||
let separator = context.tailwindConfig.separator
|
||||
let prefix =
|
||||
context.tailwindConfig.prefix !== ''
|
||||
? regex.optional(regex.pattern([/-?/, regex.escape(context.tailwindConfig.prefix)]))
|
||||
: ''
|
||||
|
||||
let utility = regex.any([
|
||||
// Arbitrary properties (without square brackets)
|
||||
/\[[^\s:'"`]+:[^\s\[\]]+\]/,
|
||||
|
||||
// Arbitrary properties with balanced square brackets
|
||||
// This is a targeted fix to continue to allow theme()
|
||||
// with square brackets to work in arbitrary properties
|
||||
// while fixing a problem with the regex matching too much
|
||||
/\[[^\s:'"`\]]+:[^\s]+?\[[^\s]+\][^\s]+?\]/,
|
||||
|
||||
// Utilities
|
||||
regex.pattern([
|
||||
// Utility Name / Group Name
|
||||
regex.any([
|
||||
/-?(?:\w+)/,
|
||||
|
||||
// This is here to make sure @container supports everything that other utilities do
|
||||
/@(?:\w+)/,
|
||||
]),
|
||||
|
||||
// Normal/Arbitrary values
|
||||
regex.optional(
|
||||
regex.any([
|
||||
regex.pattern([
|
||||
// Arbitrary values
|
||||
regex.any([
|
||||
/-(?:\w+-)*\['[^\s]+'\]/,
|
||||
/-(?:\w+-)*\["[^\s]+"\]/,
|
||||
/-(?:\w+-)*\[`[^\s]+`\]/,
|
||||
/-(?:\w+-)*\[(?:[^\s\[\]]+\[[^\s\[\]]+\])*[^\s:\[\]]+\]/,
|
||||
]),
|
||||
|
||||
// Not immediately followed by an `{[(`
|
||||
/(?![{([]])/,
|
||||
|
||||
// optionally followed by an opacity modifier
|
||||
/(?:\/[^\s'"`\\><$]*)?/,
|
||||
]),
|
||||
|
||||
regex.pattern([
|
||||
// Arbitrary values
|
||||
regex.any([
|
||||
/-(?:\w+-)*\['[^\s]+'\]/,
|
||||
/-(?:\w+-)*\["[^\s]+"\]/,
|
||||
/-(?:\w+-)*\[`[^\s]+`\]/,
|
||||
/-(?:\w+-)*\[(?:[^\s\[\]]+\[[^\s\[\]]+\])*[^\s\[\]]+\]/,
|
||||
]),
|
||||
|
||||
// Not immediately followed by an `{[(`
|
||||
/(?![{([]])/,
|
||||
|
||||
// optionally followed by an opacity modifier
|
||||
/(?:\/[^\s'"`\\$]*)?/,
|
||||
]),
|
||||
|
||||
// Normal values w/o quotes — may include an opacity modifier
|
||||
/[-\/][^\s'"`\\$={><]*/,
|
||||
])
|
||||
),
|
||||
]),
|
||||
])
|
||||
|
||||
let variantPatterns = [
|
||||
// Without quotes
|
||||
regex.any([
|
||||
// This is here to provide special support for the `@` variant
|
||||
regex.pattern([/@\[[^\s"'`]+\](\/[^\s"'`]+)?/, separator]),
|
||||
|
||||
// With variant modifier (e.g.: group-[..]/modifier)
|
||||
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]\/[\w_-]+/, separator]),
|
||||
|
||||
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]/, separator]),
|
||||
regex.pattern([/[^\s"'`\[\\]+/, separator]),
|
||||
]),
|
||||
|
||||
// With quotes allowed
|
||||
regex.any([
|
||||
// With variant modifier (e.g.: group-[..]/modifier)
|
||||
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]\/[\w_-]+/, separator]),
|
||||
|
||||
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]/, separator]),
|
||||
regex.pattern([/[^\s`\[\\]+/, separator]),
|
||||
]),
|
||||
]
|
||||
|
||||
for (const variantPattern of variantPatterns) {
|
||||
yield regex.pattern([
|
||||
// Variants
|
||||
'((?=((',
|
||||
variantPattern,
|
||||
')+))\\2)?',
|
||||
|
||||
// Important (optional)
|
||||
/!?/,
|
||||
|
||||
prefix,
|
||||
|
||||
utility,
|
||||
])
|
||||
}
|
||||
|
||||
// 5. Inner matches
|
||||
yield /[^<>"'`\s.(){}[\]#=%$][^<>"'`\s(){}[\]#=%$]*[^<>"'`\s.(){}[\]#=%:$]/g
|
||||
}
|
||||
|
||||
// We want to capture any "special" characters
|
||||
// AND the characters immediately following them (if there is one)
|
||||
let SPECIALS = /([\[\]'"`])([^\[\]'"`])?/g
|
||||
let ALLOWED_CLASS_CHARACTERS = /[^"'`\s<>\]]+/
|
||||
|
||||
/**
|
||||
* Clips a string ensuring that parentheses, quotes, etc… are balanced
|
||||
* Used for arbitrary values only
|
||||
*
|
||||
* We will go past the end of the balanced parens until we find a non-class character
|
||||
*
|
||||
* Depth matching behavior:
|
||||
* w-[calc(100%-theme('spacing[some_key][1.5]'))]']
|
||||
* ┬ ┬ ┬┬ ┬ ┬┬ ┬┬┬┬┬┬┬
|
||||
* 1 2 3 4 34 3 210 END
|
||||
* ╰────┴──────────┴────────┴────────┴┴───┴─┴┴┴
|
||||
*
|
||||
* @param {string} input
|
||||
*/
|
||||
function clipAtBalancedParens(input) {
|
||||
// We are care about this for arbitrary values
|
||||
if (!input.includes('-[')) {
|
||||
return input
|
||||
}
|
||||
|
||||
let depth = 0
|
||||
let openStringTypes = []
|
||||
|
||||
// Find all parens, brackets, quotes, etc
|
||||
// Stop when we end at a balanced pair
|
||||
// This is naive and will treat mismatched parens as balanced
|
||||
// This shouldn't be a problem in practice though
|
||||
let matches = input.matchAll(SPECIALS)
|
||||
|
||||
// We can't use lookbehind assertions because we have to support Safari
|
||||
// So, instead, we've emulated it using capture groups and we'll re-work the matches to accommodate
|
||||
matches = Array.from(matches).flatMap((match) => {
|
||||
const [, ...groups] = match
|
||||
|
||||
return groups.map((group, idx) =>
|
||||
Object.assign([], match, {
|
||||
index: match.index + idx,
|
||||
0: group,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
for (let match of matches) {
|
||||
let char = match[0]
|
||||
let inStringType = openStringTypes[openStringTypes.length - 1]
|
||||
|
||||
if (char === inStringType) {
|
||||
openStringTypes.pop()
|
||||
} else if (char === "'" || char === '"' || char === '`') {
|
||||
openStringTypes.push(char)
|
||||
}
|
||||
|
||||
if (inStringType) {
|
||||
continue
|
||||
} else if (char === '[') {
|
||||
depth++
|
||||
continue
|
||||
} else if (char === ']') {
|
||||
depth--
|
||||
continue
|
||||
}
|
||||
|
||||
// We've gone one character past the point where we should stop
|
||||
// This means that there was an extra closing `]`
|
||||
// We'll clip to just before it
|
||||
if (depth < 0) {
|
||||
return input.substring(0, match.index - 1)
|
||||
}
|
||||
|
||||
// We've finished balancing the brackets but there still may be characters that can be included
|
||||
// For example in the class `text-[#336699]/[.35]`
|
||||
// The depth goes to `0` at the closing `]` but goes up again at the `[`
|
||||
|
||||
// If we're at zero and encounter a non-class character then we clip the class there
|
||||
if (depth === 0 && !ALLOWED_CLASS_CHARACTERS.test(char)) {
|
||||
return input.substring(0, match.index)
|
||||
}
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
// Regular utilities
|
||||
// {{modifier}:}*{namespace}{-{suffix}}*{/{opacityModifier}}?
|
||||
|
||||
// Arbitrary values
|
||||
// {{modifier}:}*{namespace}-[{arbitraryValue}]{/{opacityModifier}}?
|
||||
// arbitraryValue: no whitespace, balanced quotes unless within quotes, balanced brackets unless within quotes
|
||||
|
||||
// Arbitrary properties
|
||||
// {{modifier}:}*[{validCssPropertyName}:{arbitraryValue}]
|
||||
Reference in New Issue
Block a user