如何通过编程方式渲染Stylus文件夹?求更优可重复执行方案
Great question! Since you've already got a working solution for rendering .styl files from your assets folder to CSS programmatically (instead of relying on package.json scripts), let's dive into more optimized, reusable approaches that support repeated execution—perfect for non-pre-rendering scenarios.
1. Wrap Stylus' Node.js API into a Reusable Function
The most lightweight and flexible approach is to leverage Stylus' official Node.js API directly, wrapping it into a function you can call as many times as needed. This keeps your workflow tightly integrated with your codebase without extra tooling.
Here's a clean, promise-based implementation (great for async workflows):
const stylus = require('stylus'); const fs = require('fs/promises'); const path = require('path'); async function renderStylusToCss(inputPath, outputPath) { try { // Read the Stylus file content const stylusContent = await fs.readFile(inputPath, 'utf8'); // Render to CSS const cssContent = await new Promise((resolve, reject) => { stylus(stylusContent) .set('filename', inputPath) // Required for import resolution .render((err, css) => { if (err) reject(err); else resolve(css); }); }); // Write the output CSS file await fs.writeFile(outputPath, cssContent, 'utf8'); console.log(`Successfully rendered ${inputPath} to ${outputPath}`); } catch (err) { console.error(`Error rendering Stylus file: ${err.message}`); throw err; // Re-throw if you want upstream code to handle errors } } // Example usage (call this as many times as needed) renderStylusToCss( path.join(__dirname, 'assets', 'styles.styl'), path.join(__dirname, 'dist', 'styles.css') );
Key benefits:
- Fully reusable: Call
renderStylusToCss()anywhere in your code, as often as needed (e.g., in response to user actions, API events, etc.) - Async/await support: Plays nicely with modern Node.js code
- Full control: Customize rendering options (add plugins, enable source maps, set include paths, etc.) by chaining methods to the
stylus()instance
2. Add Caching for Frequent Execution
If you're going to run this rendering logic very often (e.g., in a server context where requests trigger re-renders), adding a cache layer can avoid redundant work. You can track file modification times to only re-render when the .styl file has changed.
const fs = require('fs/promises'); const path = require('path'); // Cache to store last modified times and compiled CSS const renderCache = new Map(); async function renderStylusToCssCached(inputPath, outputPath) { const stats = await fs.stat(inputPath); const lastModified = stats.mtime.getTime(); // Check if we have a fresh cached result const cachedEntry = renderCache.get(inputPath); if (cachedEntry && cachedEntry.lastModified === lastModified) { console.log(`Using cached CSS for ${inputPath}`); await fs.writeFile(outputPath, cachedEntry.css, 'utf8'); return; } // If no cache or outdated, render fresh (reuse the render logic from approach 1) const cssContent = await (async () => { const stylusContent = await fs.readFile(inputPath, 'utf8'); return new Promise((resolve, reject) => { stylus(stylusContent) .set('filename', inputPath) .render((err, css) => err ? reject(err) : resolve(css)); }); })(); // Update the cache renderCache.set(inputPath, { lastModified, css: cssContent }); await fs.writeFile(outputPath, cssContent, 'utf8'); console.log(`Rendered fresh CSS for ${inputPath}`); }
This cuts down on unnecessary CPU usage when the source .styl file hasn't changed between executions.
3. Use a Task Runner (For Multi-Workflow Scenarios)
If your project already uses a task runner like Gulp, you can define a reusable Stylus task that you can trigger programmatically (instead of just via CLI). This is useful if you have other asset processing tasks (minification, autoprefixing, etc.) to chain alongside Stylus rendering.
Example Gulp task you can call programmatically:
const gulp = require('gulp'); const stylus = require('gulp-stylus'); function stylusTask() { return gulp.src('assets/*.styl') .pipe(stylus()) .pipe(gulp.dest('dist/css')); } // Export the task for CLI use, but also call it directly in code module.exports = { stylusTask }; // Example: Call the task programmatically stylusTask().on('end', () => { console.log('Stylus rendering completed'); });
While this adds a bit more overhead than the direct Node API, it's great if you need to integrate with other build tools or pre/post-processing steps.
Key Considerations
- Error handling: Always wrap rendering logic in try/catch (or promise error handlers) to avoid unhandled exceptions, especially if this runs in a production environment.
- Source maps: If you need debuggable CSS, add
.set('sourcemap', { inline: true })to the Stylus instance in approach 1, or use thesourcemapoption in gulp-stylus. - Plugin support: Both the Node API and Gulp plugin let you add Stylus plugins (like nib) by passing them via
.use(plugin()).
内容的提问来源于stack exchange,提问作者itsezc

