Skip to content

Commit

Permalink
Add multiple files (via dir) build interface to API (and inherently CLI)
Browse files Browse the repository at this point in the history
  • Loading branch information
t-ski committed Aug 22, 2024
1 parent b939950 commit 71a8d18
Show file tree
Hide file tree
Showing 18 changed files with 233 additions and 151 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<a href="https://github.com/t-ski/flecss">
<a href="http://t-ski.github.io/flecss" target="_blank">
<img src="./docs/img/flecss-logo.svg" width="165">
</a><br><br>

Expand Down
2 changes: 1 addition & 1 deletion docs/app.css

Large diffs are not rendered by default.

47 changes: 26 additions & 21 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -176,15 +176,15 @@ <h4><span>CLI</span> <b class="badge">Recommended</b></h4>
</thead>
<tbody>
<tr>
<td style="text-align:left"><code>--standalone</code> <code>-S</code></td>
<td style="text-align:left"><nobr><code>--standalone</code> <code>-S</code></nobr></td>
<td style="text-align:left"><code>flag</code> Build without including flecss.</td>
</tr>
<tr>
<td style="text-align:left"><code>--watch</code> <code>-W</code></td>
<td style="text-align:left"><nobr><code>--watch</code> <code>-W</code></nobr></td>
<td style="text-align:left"><code>flag</code> Watch file changes for incremental builds.</td>
</tr>
<tr>
<td style="text-align:left"><code>--library</code> <code>-L</code></td>
<td style="text-align:left"><nobr><code>--library</code> <code>-L</code></nobr></td>
<td style="text-align:left"><code>option</code> Specify the flecss library to compile with (see above, default is <code>flecss.css</code>).</td>
</tr>
</tbody>
Expand All @@ -193,29 +193,34 @@ <h4>Example</h4>
<source-code language="console">npx flecss ./scss ./public/app.css --watch
</source-code>
<h4>API</h4>
<source-code language="ts">interface IBuildOptions {
isDevelopment?: boolean;
isStandalone?: boolean;
<source-code language="ts">interface ITranspilerOptions {
isDevelopment?: boolean; // do not optimise output
isStandalone?: boolean; // do not integrate flecss core
library?: &quot;flecss&quot;|&quot;flecss.min&quot;|&quot;flecss.min.shorthand&quot;,
loadPaths?: string[];
}
</source-code>
<source-code language="ts">function build(sourcePath: string, targetPath?: string, options?: IBuildOptions &amp; {
modTimeTolerance?: number;
}): Promise&lt;{
executionTimeMs: number;
sourcePath: string;
targetPath: string;
targetSizeByte: number;
}&gt;;
</source-code>
<source-code language="ts">function transpile(source: string, options?: IBuildOptions): {

interface ITranspilerOutput {
css: string;
loadedUrls: string[];
}
</source-code>
<source-code language="ts">// Comprehensive
function build(sourcePath: string, targetPath?: string, options?: ITranspilerOptions): Promise&lt;{
executionTimeMs: number;
transpilerOutput: ITranspilerOutput|ITranspilerOutput[];
}&gt;;

// Atomic
function transpileSCSS(sourceSCSS: string, options?: ITranspilerOptions): ITranspilerOutput;
function transpileFile(sourcePath: string, options?: ITranspilerOptions): ITranspilerOutput;
</source-code>
<h4>Example</h4>
<source-code language="js">const flecss = require(&quot;flecss&quot;);

flecss.build(&quot;./app.scss&quot;); // target path: app.css
flecss.build(&quot;./app.scss&quot;, {
library: &quot;flecss.min&quot;
}); // target path: app.css
</source-code>
</section>
<section class="card"><h2>Classes</h2>
Expand Down Expand Up @@ -416,7 +421,7 @@ <h4>Example</h4>
<hr>

<h3><code>.p</code> <code>.padding</code></h3>
<p>The padding class simply helps with inducing a margin. It works analogous to the margin class.</p>
<p>The padding class simply helps with inducing a padding. It works analogous to the margin class.</p>
<div class="synopsis">
<table class="synopsis-syntax">
<tr>
Expand Down Expand Up @@ -504,7 +509,7 @@ <h4>Example</h4>
<hr>

<h3><code>.s</code> <code>.section</code> <span>+</span> <code>.w</code> <code>.wrapper</code></h3>
<p>The section and wrapper class are vital to vertical layouting. In fact, each class represents a nestable container. A section stretches across the full width, with a small affixed vertical content padding as a safety area. The wrapper has a limited width and is centered within a section, with an extra large affixed horizontal content padding to separate stacked content. Used in combination, the section-wrapper layouting classes provide a simple yet powerful styling capabilities.</p>
<p>The section and wrapper classes are vital to vertical layouting. In fact, each class represents a nestable container. A section stretches across the full width, with a small affixed horizontal content padding as a safety area. The wrapper has a limited width and is centered within a section, with an extra large affixed vertical content padding to separate stacked content in a linear vertical layout. Used in combination, the section-wrapper layouting classes provide simple yet powerful styling capabilities.</p>
<div class="synopsis">
<div class="synopsis-figures">
<div>
Expand Down Expand Up @@ -718,7 +723,7 @@ <h4>Example</h4>
</tr>
</table>
</div>
<p>The size of a space unit (e.g. <code>xs</code>) scales progressively aroud the viewport lib space unit (<code>--flecss__space--&lt;s|m|l&gt;</code>). It is scaled by a constant factor <code>--flecss__space-factor</code> by the power of the unit (…, <code>s</code>: = -1, <code>m</code> = 0, <code>l</code> = 1, <code>xl</code> = 2, …). For instance on large viewports, <code>xl</code> corresponds to <code>--flecss__space--l * --flecss__space-factor^2</code> – i.e. <code>~5.35rem</code> by default.</p>
<p>The size of a space unit (e.g. <code>xs</code>) scales progressively aroud the viewport core space unit (<code>--flecss__space--&lt;s|m|l&gt;</code>). It is scaled by a constant factor <code>--flecss__space-factor</code> by the power of the unit (…, <code>s</code>: = -1, <code>m</code> = 0, <code>l</code> = 1, <code>xl</code> = 2, …). For instance on large viewports, <code>xl</code> corresponds to <code>--flecss__space--l * --flecss__space-factor^2</code> – i.e. <code>~5.35rem</code> by default.</p>
<table>
<tr>
<td><code>--space-factor</code></td>
Expand Down
41 changes: 22 additions & 19 deletions documentation/1. Integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ npx flecss <path:source> <path:target> [--<arg>|-<arg:shorthand> *?]*

| Argument | Description |
| :- | :- |
| `--standalone` `-S` | `flag` Build without including flecss. |
| `--watch` `-W` | `flag` Watch file changes for incremental builds. |
| `--library` `-L` | `option` Specify the flecss library to compile with (see above, default is `flecss.css`). |
| <nobr><code>--standalone</code> <code>-S</code></nobr> | `flag` Build without including flecss. |
| <nobr><code>--watch</code> <code>-W</code></nobr> | `flag` Watch file changes for incremental builds. |
| <nobr><code>--library</code> <code>-L</code></nobr> | `option` Specify the flecss library to compile with (see above, default is `flecss.css`). |

#### Example

Expand All @@ -81,34 +81,37 @@ npx flecss ./scss ./public/app.css --watch
#### API

``` ts
interface IBuildOptions {
isDevelopment?: boolean;
isStandalone?: boolean;
interface ITranspilerOptions {
isDevelopment?: boolean; // do not optimise output
isStandalone?: boolean; // do not integrate flecss core
library?: "flecss"|"flecss.min"|"flecss.min.shorthand",
loadPaths?: string[];
}

interface ITranspilerOutput {
css: string;
loadedUrls: string[];
}
```

``` ts
function build(sourcePath: string, targetPath?: string, options?: IBuildOptions & {
modTimeTolerance?: number;
}): Promise<{
// Comprehensive
function build(sourcePath: string, targetPath?: string, options?: ITranspilerOptions): Promise<{
executionTimeMs: number;
sourcePath: string;
targetPath: string;
targetSizeByte: number;
transpilerOutput: ITranspilerOutput|ITranspilerOutput[];
}>;
```

``` ts
function transpile(source: string, options?: IBuildOptions): {
css: string;
loadedUrls: string[];
}
// Atomic
function transpileSCSS(sourceSCSS: string, options?: ITranspilerOptions): ITranspilerOutput;
function transpileFile(sourcePath: string, options?: ITranspilerOptions): ITranspilerOutput;
```

#### Example

``` js
const flecss = require("flecss");

flecss.build("./app.scss"); // target path: app.css
flecss.build("./app.scss", {
library: "flecss.min"
}); // target path: app.css
```
2 changes: 1 addition & 1 deletion documentation/2. Classes/4. `.p` Padding.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### `.p` `.padding`

The padding class simply helps with inducing a margin. It works analogous to the margin class.
The padding class simply helps with inducing a padding. It works analogous to the margin class.

<div class="synopsis">
<table class="synopsis-syntax">
Expand Down
2 changes: 1 addition & 1 deletion documentation/2. Classes/5. `.s` Section etc..md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### `.s` `.section` <span>+</span> `.w` `.wrapper`

The section and wrapper class are vital to vertical layouting. In fact, each class represents a nestable container. A section stretches across the full width, with a small affixed vertical content padding as a safety area. The wrapper has a limited width and is centered within a section, with an extra large affixed horizontal content padding to separate stacked content. Used in combination, the section-wrapper layouting classes provide a simple yet powerful styling capabilities.
The section and wrapper classes are vital to vertical layouting. In fact, each class represents a nestable container. A section stretches across the full width, with a small affixed horizontal content padding as a safety area. The wrapper has a limited width and is centered within a section, with an extra large affixed vertical content padding to separate stacked content in a linear vertical layout. Used in combination, the section-wrapper layouting classes provide simple yet powerful styling capabilities.

<div class="synopsis">
<div class="synopsis-figures">
Expand Down
2 changes: 1 addition & 1 deletion documentation/3. Space Modifier.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ The space modifier is quite a new idea in flecss. It allows to alternate the spa
</table>
</div>

The size of a space unit (e.g. `xs`) scales progressively aroud the viewport lib space unit (`--flecss__space--<s|m|l>`). It is scaled by a constant factor `--flecss__space-factor` by the power of the unit (…, `s`: = -1, `m` = 0, `l` = 1, `xl` = 2, …). For instance on large viewports, `xl` corresponds to `--flecss__space--l * --flecss__space-factor^2` – i.e. `~5.35rem` by default.
The size of a space unit (e.g. `xs`) scales progressively aroud the viewport core space unit (`--flecss__space--<s|m|l>`). It is scaled by a constant factor `--flecss__space-factor` by the power of the unit (…, `s`: = -1, `m` = 0, `l` = 1, `xl` = 2, …). For instance on large viewports, `xl` corresponds to `--flecss__space--l * --flecss__space-factor^2` – i.e. `~5.35rem` by default.

<table>
<tr>
Expand Down
128 changes: 97 additions & 31 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,16 @@ const sass = require("sass");
const CleanCSS = require("clean-css");


function transpile(source, options = {}) {
function transpileSCSS(sourceSCSS, options = {}) {
const optionsWithDefaults = {
isDevelopment: false,
isStandalone: false,
library: "flecss",
loadPaths: [],

...options
};

const sourceIsPath = /^.*\.scss$/i.test(source);
if(sourceIsPath && !fs.existsSync(source)) {
throw new ReferenceError(`File does not exist at ${source}`);
}
const sourceSCSS = sourceIsPath
? fs.readFileSync(source).toString()
: source;

const scss = [ sourceSCSS ];
if(!optionsWithDefaults.isStandalone) {
const libraryPath = path.join(__dirname, "../dist/", optionsWithDefaults.library);
Expand All @@ -31,13 +24,13 @@ function transpile(source, options = {}) {
scss.unshift(`@import "${path.join(__dirname, "../dist/flecss.util")}";`) // utils
scss.unshift(`@import "${libraryPath}";`) // lib
}

const sassResult = sass.compileString(scss.join("\n"), {
loadPaths: sourceIsPath ? [ path.dirname(source) ]: [],
loadPaths: optionsWithDefaults.loadPaths,
style: optionsWithDefaults.isDevelopment ? "expanded" : "compressed",
quietDeps: true
});

return {
css: !optionsWithDefaults.isDevelopment
? new CleanCSS({
Expand All @@ -49,6 +42,20 @@ function transpile(source, options = {}) {
}
}

function transpileFile(sourcePath, options = {}) {
const optionsWithDefaults = {
loadPaths: [ path.dirname(sourcePath) ],

...options
};

if(!fs.existsSync(sourcePath)) {
throw new ReferenceError(`File does not exist at ${source}`);
}

return transpileSCSS(fs.readFileSync(sourcePath).toString(), optionsWithDefaults);
}


module.exports.build = function(sourcePath, targetPath, options = {}) {
const optionsWithDefaults = {
Expand All @@ -57,27 +64,83 @@ module.exports.build = function(sourcePath, targetPath, options = {}) {
...options
};

const sourceFilePath = path.resolve(sourcePath).replace(/(\.scss)?$/i, ".scss");
if(!/\.scss/i.test(sourceFilePath)) throw new SyntaxError(`Source file does not have .scss extension '${path.basename(sourceFilePath)}'`);
if(!fs.existsSync(sourceFilePath)) throw new ReferenceError(`Source file does not exist '${sourceFilePath}'`);

const targetFilePath = (targetPath ?? `${path.dirname(sourceFilePath)}/`)
.replace(/\/$/, `/${path.basename(sourceFilePath)}`)
.replace(/(\.s?css)?$/i, ".css");
fs.mkdirSync(path.dirname(targetFilePath), { recursive: true });
const absoluteSourcePath = path.resolve(sourcePath);
if(!fs.existsSync(absoluteSourcePath)) {
throw new ReferenceError(`Source path does not exist at ${absoluteSourcePath}`);
}

const sourceIsDir = fs.statSync(sourcePath).isDirectory();
if(!sourceIsDir
&& !/\.scss$/i.test(sourcePath)) {
throw new SyntaxError(`Invalid source file (expects *.scss, got ${path.basename(sourcePath)})`);
}

const absoluteSourcePaths = sourceIsDir
? fs.readdirSync(absoluteSourcePath, {
withFileTypes: true
})
.filter((dirent) => dirent.isFile())
.filter((dirent) => /^[^_].*\.scss$/.test(dirent.name))
.map((dirent) => path.join(dirent.parentPath, dirent.name))
: [ absoluteSourcePath ];

const absoluteTargetPath = path.resolve(
targetPath
?? (!sourceIsDir ? path.dirname(absoluteSourcePath) : absoluteSourcePath)
);
const targetIsDir = !/\.css$/i.test(absoluteTargetPath);
fs.mkdirSync(targetIsDir ? absoluteTargetPath : path.dirname(absoluteTargetPath), {
recursive: true
});

return new Promise((resolve, reject) => {
const resolveEmpty = () => resolve({
executionTimeMs: 0,
transpilerOutput: null
});

if(!absoluteSourcePaths.length) {
resolveEmpty();

return;
}

const hrStart = process.hrtime();

const render = () => {
const transpiled = transpile(sourceFilePath, optionsWithDefaults);
fs.writeFile(targetFilePath, transpiled.css, null, (err) => {
err ? reject(err) : resolve({
executionTimeMs: Math.round(process.hrtime(hrStart)[1] / 1e6),
sourcePath: sourceFilePath,
targetPath: targetFilePath,
targetSizeByte: fs.statSync(targetFilePath).size
});
let builtFiles = 0;
const results = [];
absoluteSourcePaths
.forEach((absoluteSourcePath) => {
const transpiled = transpileFile(absoluteSourcePath, optionsWithDefaults);
const targetPath = targetIsDir
? path.join(
absoluteTargetPath,
absoluteSourcePath
.match(/[^\\/]+$/)[0]
.replace(/(\.scss)?$/i, ".css")
)
: absoluteTargetPath;

targetIsDir
&& fs.rmSync(targetPath);
fs.appendFile(
targetPath,
transpiled.css,
null,
(err) => {
err && reject(err);
results.push({
sourcePath: absoluteSourcePath,
targetPath: targetPath,
targetSizeByte: fs.statSync(targetPath).size
});
(++builtFiles === absoluteSourcePaths.length)
&& resolve({
executionTimeMs: Math.round(process.hrtime(hrStart)[1] / 1e6),
transpilerOutput: sourceIsDir ? results : results[0]
});
});
});
};

Expand All @@ -87,7 +150,9 @@ module.exports.build = function(sourcePath, targetPath, options = {}) {
return;
}

const dirents = fs.readdirSync(path.dirname(sourceFilePath), {
const dirents = fs.readdirSync(
sourceIsDir ? absoluteSourcePath : path.dirname(absoluteSourcePath),
{
recursive: true,
withFileTypes: true,
})
Expand All @@ -104,8 +169,9 @@ module.exports.build = function(sourcePath, targetPath, options = {}) {
return;
}

resolve(null);
resolveEmpty();
});
}

module.exports.transpile = transpile;
module.exports.transpileSCSS = transpileSCSS;
module.exports.transpileFile = transpileFile;
Loading

0 comments on commit 71a8d18

Please sign in to comment.