-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of https://github.com/clarityflowers/resplendence
- Loading branch information
Showing
12 changed files
with
4,797 additions
and
127 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,69 @@ | ||
const makeClassName = require("./make-classname"); | ||
const loaderUtils = require("loader-utils"); | ||
const getImportName = require("./get-import-name"); | ||
const getRegex = require("./get-regex"); | ||
const { importRegex, getRegex, makeClassName } = require("./util"); | ||
|
||
/** | ||
* Strip all the CSS out of the js file and add an import for | ||
* itself with resplendence=true | ||
* | ||
* @this {import('./util').LoaderContext} | ||
* | ||
* @param {string} source - The source code of the file that will be transformed | ||
*/ | ||
const scriptLoader = function(source) { | ||
const { src } = loaderUtils.getOptions(this) || {}; | ||
/** @type string */ | ||
const srcDir = (loaderUtils.getOptions(this) || {}).src; | ||
|
||
const importName = getImportName(source); | ||
if (!importName) return this.callback(null, source); | ||
const importMatch = source.match(importRegex); | ||
if (!importMatch) { | ||
// If they didn't import resplendence, don't do any processing at all. | ||
this.callback(null, source); | ||
return; | ||
} | ||
const importName = importMatch[1]; | ||
const regex = getRegex(importName); | ||
|
||
let count = 0; | ||
let output = source.replace(regex, (_m, parens, args) => { | ||
|
||
let hasComponents = false; | ||
let hasStyles = false; | ||
let output = source.replace(regex, (_all, parens, args, content) => { | ||
hasStyles = true; | ||
// Wrap the style code in comments, making sure to filter out any multiline | ||
// comments that might already be in there to avoid issues with nesting. | ||
const comment = `/*${content.replace("/*", "~*").replace("*/", "*~")}*/`; | ||
if (parens) { | ||
const className = makeClassName(this.resourcePath, ++count, src); | ||
const className = makeClassName(this.resourcePath, ++count, srcDir); | ||
if (args && args.trim()) { | ||
return `${importName}(${args}, "${className}")`; | ||
// This is a styled component --- rx("div")`` | ||
hasComponents = true; | ||
return `${importName}(${args}, "${className}")${comment}`; | ||
} else { | ||
// This is a styled classname --- rx()`` | ||
return `"${className}"${comment}`; | ||
} | ||
return `"${className}"`; | ||
} else { | ||
// This is a bare style --- rx`` | ||
return comment; | ||
} | ||
return ""; | ||
}); | ||
|
||
if (count) { | ||
output = `import "${this.resourcePath}?resplendence=true";\n${output}`; | ||
if (!hasStyles) { | ||
// Whoops, even though they imported rx, they didn't actually use it! | ||
this.callback(null, source); | ||
return; | ||
} | ||
|
||
return this.callback(null, output); | ||
output = output.replace(importRegex, all => { | ||
// The file imports itself, but with the query param resplendence=true so | ||
// that webpack can know to interpret it as a style instead of a script. | ||
let result = `import "${this.resourcePath}?resplendence=true";`; | ||
if (hasComponents) { | ||
result += all; | ||
} | ||
return result; | ||
}); | ||
|
||
this.callback(null, output); | ||
}; | ||
|
||
module.exports = scriptLoader; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,61 @@ | ||
const makeClassName = require("./make-classname"); | ||
const loaderUtils = require("loader-utils"); | ||
const getRegex = require("./get-regex"); | ||
const getImportName = require("./get-import-name"); | ||
const { importRegex, getRegex, makeClassName } = require("./util"); | ||
|
||
// Big strings that we are hoping don't show up in the user's code anywhere | ||
// Because we'll be doing find/replaces on them | ||
const DELETE_START = "!RESPLENDENCE CODE TO DELETE START!"; | ||
const DELETE_END = "!RESPLENDENCE CODE TO DELETE END!"; | ||
|
||
/** | ||
* Transform a js file with resplendence=true into a CSS file. | ||
* | ||
* @this {import('./util').LoaderContext} | ||
* | ||
* @param {string} source | ||
*/ | ||
const styleLoader = function(source) { | ||
const { src } = loaderUtils.getOptions(this) || {}; | ||
|
||
if (!this.resourceQuery.includes("resplendence=true")) { | ||
return this.callback(null, source); | ||
this.callback(null, source); | ||
return; | ||
} | ||
|
||
const importName = getImportName(source); | ||
if (!importName) return this.callback(null, source); | ||
const importMatch = source.match(importRegex); | ||
if (!importMatch) { | ||
this.callback(null, source); | ||
return; | ||
} | ||
const importName = importMatch[1]; | ||
const regex = getRegex(importName); | ||
|
||
let result = ""; | ||
let match; | ||
let count = 0; | ||
while ((match = regex.exec(source))) { | ||
if (match[1]) { | ||
// Wrap everything in a flag that this code should be deleted... | ||
let result = DELETE_START + source + DELETE_END; | ||
|
||
result = result.replace(regex, (_all, parens, _args, content) => { | ||
let code = content; | ||
if (parens) { | ||
const className = makeClassName(this.resourcePath, ++count, src); | ||
result += `.${className} {\n${match[3]}\n}\n`; | ||
} else { | ||
result += match[3] + "\n"; | ||
code = `.${className} {${content}}`; | ||
} | ||
} | ||
// ... then carve out exceptions for the css ... | ||
return DELETE_END + code + DELETE_START; | ||
}); | ||
|
||
// ... then strip all of the code marked for deletion, | ||
// leaving only the newlines, to preserve line numbers ... | ||
result = result.replace( | ||
new RegExp(`${DELETE_START}([\\s\\S]*?)${DELETE_END}`, "g"), | ||
(_all, code) => { | ||
return code.replace(/[\S \t]+/g, ""); | ||
} | ||
); | ||
|
||
// ... and finally, trim trailing whitespace | ||
result = result.replace(/\s+$/, ""); | ||
|
||
return this.callback(null, result); | ||
this.callback(null, result); | ||
}; | ||
|
||
module.exports = styleLoader; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
const path = require("path"); | ||
|
||
/** | ||
* Add a source file, and optionally a map | ||
* | ||
* @callback loaderCallback | ||
* @param {?Error} error | ||
* @param {string} code | ||
* @param {Object=} sourceMap | ||
* @param {any=} any | ||
*/ | ||
|
||
/** | ||
* The context passed into a loader | ||
* | ||
* @typedef {Object} LoaderContext | ||
* @property {string} resourcePath | ||
* @property {loaderCallback} callback | ||
* @property {string|Object|null} query | ||
* @property {boolean} sourceMap | ||
* @property {string} resourceQuery | ||
*/ | ||
|
||
/** | ||
* Produce a classname from the path relative to the configured source directory. | ||
* @param {string} filepath the file's path | ||
* @param {number} index the current count of classnames added for this file | ||
* @param {string} src the configured source directory | ||
*/ | ||
exports.makeClassName = function(filepath, index, src) { | ||
const name = path | ||
.relative(src, filepath) | ||
.replace(/[/\\]/g, "-") | ||
.replace(/\..*$/, ""); | ||
return `rx-${name}-${index}`; | ||
}; | ||
|
||
/** | ||
* Gets the regex that looks for resplendent styles. | ||
* @param {string} importName | ||
*/ | ||
exports.getRegex = importName => | ||
new RegExp(importName + "(\\((.*?)?\\))?`((.|[\\s\\S])*?)`", "g"); | ||
|
||
/** | ||
* regex to fetch the line where resplendence is imported. If you don't import | ||
* resplendence, then the file won't be processed at all. | ||
*/ | ||
exports.importRegex = /import +(\S+?) +from +['"]resplendence['"] *;?/; |
Oops, something went wrong.