forked from PreTeXtBook/pretext
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CSS builder script and ignore node_modules
- Loading branch information
1 parent
2fb780c
commit 3539443
Showing
5 changed files
with
1,641 additions
and
0 deletions.
There are no files selected for viewing
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 |
---|---|---|
|
@@ -16,3 +16,4 @@ examples/webwork/minimal/generated/* | |
**/cli.log | ||
**/rsbuild | ||
**/build | ||
**/node_modules/ |
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,35 @@ | ||
# CSS Builder | ||
|
||
## Installing Node and Dependencies | ||
|
||
You will need to [install node](https://nodejs.org/en/download/package-manager). | ||
|
||
Install the needed dependencies by switching to the `pretext/script/cssbuilder` and doing `npm install`. | ||
|
||
## Use | ||
|
||
To build all targets to `pretext/css/dist`, from the cssbuilder directory do: | ||
|
||
```bash | ||
npm run build | ||
``` | ||
|
||
For debugging, you likely want to build one target (`default-modern` in this case) to a specified output directory (generally the `_static/pretext/css` folder of your book), rebuilding with any changes (`-w` for "watch"). That can be done with: | ||
|
||
```bash | ||
npm run build -- -t default-modern -o yourbookpath/_static/pretext/css -w' | ||
``` | ||
To specify options or variables you can add the `-c` flag followed by a string containing JSON like this: | ||
```bash | ||
npm run build -- -w -t theme-default-modern -o ../../examples/sample-article/out/_static/pretext/css -c '{"options":{"assemblages":"oscar-levin"},"variables":{"primary-color":"rgb(80, 30, 80)", "secondary-color":"rgb(20, 160, 30)", "primary-color-dark":"#b88888"}}' | ||
``` | ||
For full help: | ||
```bash | ||
npm run build -- -h | ||
``` | ||
Also see [README.md in css](../../css/README.md) |
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,239 @@ | ||
import esbuild from 'esbuild'; | ||
import { sassPlugin } from 'esbuild-sass-plugin'; | ||
import commandLineArgs from 'command-line-args'; | ||
import commandLineUsage from 'command-line-usage'; | ||
import path from 'path'; | ||
import * as url from 'url'; | ||
|
||
// Path to pretext/css relative to the pretext/script/cssbuilder directory | ||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); | ||
const cssRoot = path.join(__dirname, '../../css/'); | ||
|
||
function getOptions() { | ||
const optionDefinitions = [ | ||
{ name: 'output-directory', alias: 'o', type: String }, | ||
{ name: 'watch', alias: 'w', type: Boolean }, | ||
{ name: 'selected-target', alias: 't', type: String }, | ||
{ name: 'config-options', alias: 'c', type: String }, | ||
{ name: 'list-targets', alias: 'l', type: Boolean }, | ||
{ name: 'help', alias: 'h', type: Boolean }, | ||
{ name: 'verbose', alias: 'v', type: Boolean }, | ||
] | ||
let configs = commandLineArgs(optionDefinitions); | ||
|
||
if (configs['config-options']) { | ||
// Convert the JSON string to an object | ||
const configOptions = JSON.parse(configs['config-options']); | ||
configs['config-options'] = configOptions; | ||
} | ||
|
||
return configs; | ||
} | ||
|
||
const helpContents = [ | ||
{ | ||
header: 'PreTeXt CSS Builder', | ||
content: 'Generates CSS files for PreTeXt themes. By default, all build targets are built to the css/dist directory.' | ||
}, | ||
{ | ||
header: 'Options', | ||
optionList: [ | ||
{ | ||
name: 'config-options', | ||
typeLabel: '{underline json-text}', | ||
description: 'A string containing a JSON blob with configuration options for the build. This includes variables and options for building a customized version of a theme. This might look like {bold \'\\{"options": \\{"assemblages": "oscar-levin"\\}, "variables": \\{"primary-color": "#801811", "primary-color-dark": "#801811", "secondary-color": "#2a5ea4"\\}\\}\'}.', | ||
}, | ||
{ | ||
name: 'help', | ||
description: 'Print this usage guide.', | ||
alias: 'h', | ||
}, | ||
{ | ||
name: 'list-targets', | ||
description: 'List all build targets.', | ||
alias: 'l', | ||
}, | ||
{ | ||
name: 'output-directory', | ||
description: 'Directory to place output in. Can be absolute or relative to the {bold pretext/script/cssbuilder/} directory.', | ||
alias: 'o', | ||
}, | ||
{ | ||
name: 'selected-target', | ||
description: 'Which one target to build. Use {underline list-targets} option to see available targets. If there is an {underline output-directory} set and the target is not a module, the target will be build with the name {bold theme.css} regardless of the input target name.', | ||
alias: 't', | ||
}, | ||
{ | ||
name: 'verbose', | ||
description: 'Print extra output.', | ||
alias: 'v', | ||
}, | ||
{ | ||
name: 'watch', | ||
description: 'Continuously watch for changes to css/scss and rebuild.', | ||
alias: 'w', | ||
} | ||
] | ||
}, | ||
{ | ||
header: 'Usage', | ||
content: [ | ||
{ | ||
desc: 'Build one or more themes. Note that options must be separated from the command with --.', | ||
example: '$ npm run build [-- options...]' | ||
}, | ||
{}, | ||
{ | ||
desc: 'Build one target to a specified output directory, rebuilding with any changes.', | ||
example: '$ npm run build -- -t theme-default-modern -o /path/to/output -w' | ||
}, | ||
] | ||
}, | ||
] | ||
|
||
function getOutDir(options) { | ||
if (options['output-directory']) | ||
return options['output-directory']; | ||
else | ||
return path.join(cssRoot, 'dist'); | ||
} | ||
|
||
function getTargets(options) { | ||
let targets = [ | ||
// ------------------------------------------------------------------------- | ||
// Web targets - pretext assumes output name will be 'theme-XXX' | ||
// Legacy targets | ||
{ out: 'theme-default-legacy', in: path.join(cssRoot, 'targets/html/legacy/default/theme-default.scss'), autobuild: true }, | ||
{ out: 'theme-min-legacy', in: path.join(cssRoot, 'targets/html/legacy/min/theme-min.scss'), autobuild: true }, | ||
{ out: 'theme-crc-legacy', in: path.join(cssRoot, 'targets/html/legacy/crc/theme-crc.scss'), autobuild: true }, | ||
{ out: 'theme-soundwriting-legacy', in: path.join(cssRoot, 'targets/html/legacy/soundwriting/theme-soundwriting.scss'), autobuild: true }, | ||
{ out: 'theme-wide-legacy', in: path.join(cssRoot, 'targets/html/legacy/wide/theme-wide.scss'), autobuild: true }, | ||
{ out: 'theme-oscarlevin-legacy', in: path.join(cssRoot, 'targets/html/legacy/oscarlevin/theme-oscarlevin.scss'), autobuild: true }, | ||
// ------------------------------------------------------------------------- | ||
// Modern web targets | ||
{ out: 'theme-default-modern', in: path.join(cssRoot, 'targets/html/default-modern/theme-default-modern.scss'), autobuild: true }, | ||
// only default is prebuilt | ||
{ out: 'theme-salem', in: path.join(cssRoot, 'targets/html/salem/theme-salem.scss')}, | ||
{ out: 'theme-denver', in: path.join(cssRoot, 'targets/html/denver/theme-denver.scss') }, | ||
{ out: 'theme-greeley', in: path.join(cssRoot, 'targets/html/greeley/theme-greeley.scss') }, | ||
{ out: 'theme-tacoma', in: path.join(cssRoot, 'targets/html/tacoma/theme-tacoma.scss') }, | ||
// ------------------------------------------------------------------------- | ||
// ------------------------------------------------------------------------- | ||
// Non-web targets | ||
{ out: 'reveal', in: path.join(cssRoot, 'targets/revealjs/reveal.scss'), autobuild: true }, | ||
{ out: 'kindle', in: path.join(cssRoot, 'targets/ebook/kindle/kindle.scss'), autobuild: true }, | ||
{ out: 'epub', in: path.join(cssRoot, 'targets/ebook/epub/epub.scss'), autobuild: true }, | ||
] | ||
|
||
if (options['selected-target']) { | ||
// Build the one selected target | ||
if(options['selected-target'] !== 'theme-custom') { | ||
targets = targets.filter(target => target.out === options['selected-target']); | ||
} else { | ||
// Custom theme build | ||
const configOptions = options['config-options']; | ||
// console.log('configOptions', configOptions); | ||
if (configOptions && configOptions['options'] && configOptions['options']['entry-point']) { | ||
// Custom theme build with output directory | ||
targets = [ | ||
{ out: 'theme-custom', in: configOptions['options']['entry-point'], autobuild: true } | ||
] | ||
// Remove the entry-point from the options so it doesn't get turned into scss variable | ||
delete configOptions['options']['entry-point']; | ||
} else { | ||
// Custom theme build without output directory | ||
console.error('Custom theme build requires an entry-point config option. It should be the path to the custom theme SCSS file.'); | ||
process.exit(1); | ||
} | ||
} | ||
|
||
if (targets.length === 0) { | ||
console.error('Selected target not found'); | ||
process.exit(1); | ||
} else { | ||
// Change the output name if an output directory is set (assume we building directly to book) | ||
if (options['output-directory']) { | ||
const targetName = targets[0].out; | ||
if (targetName.includes('modules/')) { | ||
// Modules build directly to destination with no subfolder | ||
targets[0].out = targets[0].out.replace('modules/', ''); | ||
} else { | ||
// Others build as theme.css | ||
targets[0].out = 'theme'; | ||
} | ||
} | ||
} | ||
} else { | ||
// Only build targets that have the autobuild flag set | ||
targets = targets.filter(target => target.autobuild); | ||
} | ||
|
||
// Clear the autobuild value from the targets. ESBuild just wants in/out | ||
targets = targets.map(target => { | ||
delete target.autobuild; | ||
return target; | ||
}); | ||
|
||
return targets; | ||
} | ||
|
||
async function getESBuildConfig(options) { | ||
const targets = getTargets(options); | ||
const outDir = getOutDir(options); | ||
// Only minify if there is only one target | ||
// when building all the prebuilt themes to dist, we want to preserve new lines | ||
// as the files are going into git | ||
const minifyCSS = targets.length === 1; | ||
const ctx = await esbuild | ||
.context({ | ||
entryPoints: targets, | ||
bundle: true, | ||
sourcemap: true, | ||
minify: minifyCSS, | ||
outdir: outDir, | ||
format: 'esm', | ||
plugins: [ | ||
sassPlugin({ | ||
'loadPaths': [cssRoot], | ||
precompile(source, pathname, isRoot) { | ||
// Tack on any config variables to the top of the file | ||
let prefix = ''; | ||
if (options['selected-target'] && options['config-options']) { | ||
for (const [key, value] of Object.entries(options['config-options']['options'])) { | ||
prefix += `$${key}: ${value};\n`; | ||
} | ||
} | ||
return isRoot ? prefix + source : source; | ||
} | ||
}), | ||
], | ||
metafile: true, | ||
logLevel: 'info', | ||
}); | ||
return ctx; | ||
} | ||
|
||
// -------------------------------------------------------------------------- | ||
// Main | ||
const options = getOptions(); | ||
if (options['help']) { | ||
console.log(commandLineUsage(helpContents)); | ||
} else if (options['list-targets']) { | ||
const targets = getTargets({}); | ||
console.log('Available targets:'); | ||
for (const target of targets) { | ||
console.log(target.out); | ||
} | ||
} else { | ||
// Actual build | ||
const ctx = await getESBuildConfig(options); | ||
if (options['verbose']) console.log("CSSBuilder options", options); | ||
|
||
if (options.watch) { | ||
await ctx.watch(); | ||
} else { | ||
await ctx.rebuild(); | ||
await ctx.dispose(); | ||
console.log('CSS build complete!'); | ||
} | ||
} |
Oops, something went wrong.