11#!/usr/bin/env node
2- import { compileString } from "sass-embedded" ;
2+ import { compileString } from "sass-embedded" ;
3+ import readline from "readline" ;
34
45/**
56 * Optimized bridge for sass compilation with support for:
@@ -9,14 +10,13 @@ import { compileString } from "sass-embedded";
910 * - Memory-efficient processing
1011 */
1112
12- // Check for persistent mode
13- const isPersistent = process . argv . includes ( '--persistent' ) ;
13+ /** @type {string[] } */
14+ const argv = process . argv ;
15+ const isPersistent = argv . includes ( '--persistent' ) ;
1416
1517if ( isPersistent ) {
16- // Persistent mode: process multiple requests
1718 processPersistentMode ( ) ;
1819} else {
19- // Single request mode (legacy)
2020 processSingleRequest ( ) ;
2121}
2222
@@ -43,80 +43,139 @@ function processSingleRequest() {
4343 process . stdin . on ( "end" , ( ) => {
4444 try {
4545 const payload = JSON . parse ( buffer . join ( '' ) || "{}" ) ;
46- const source = String ( payload . source || "" ) ;
47- const options = payload . options || { } ;
48- const url = payload . url ? new URL ( String ( payload . url ) ) : undefined ;
49- const compileOpts = { } ;
46+ const response = compilePayload ( payload ) ;
5047
51- if ( url ) compileOpts . url = url ;
52-
53- if ( options . syntax === 'sass' || options . syntax === 'indented' ) {
54- compileOpts . syntax = 'indented' ;
48+ process . stdout . write ( JSON . stringify ( response ) ) ;
49+ } catch ( err ) {
50+ process . stdout . write ( JSON . stringify ( { error : String ( err ?. message || err ) } ) ) ;
5551 }
52+ } ) ;
53+ }
5654
57- if ( options . minimize || ( 'compressed' in options && options . compressed ) || options . style === 'compressed' ) {
58- compileOpts . style = 'compressed' ;
59- }
55+ /**
56+ * Generator function for processing compilation results in chunks
57+ * Useful for memory-efficient handling of large CSS outputs
58+ */
59+ function * cssChunkGenerator ( css , chunkSize = 64 * 1024 ) {
60+ for ( let i = 0 ; i < css . length ; i += chunkSize ) {
61+ yield css . slice ( i , i + chunkSize ) ;
62+ }
63+ }
6064
61- if ( options . sourceMap || options . sourceMapPath ) {
62- compileOpts . sourceMap = options . sourceMapPath || options . sourceMap ;
65+ /**
66+ * Generator function for processing sourceMap in chunks
67+ * Useful for large sourceMap data
68+ */
69+ function * sourceMapChunkGenerator ( sourceMap , chunkSize = 64 * 1024 ) {
70+ const mapString = JSON . stringify ( sourceMap ) ;
6371
64- if ( options . includeSources ) {
65- compileOpts . sourceMapIncludeSources = options . includeSources ;
72+ for ( let i = 0 ; i < mapString . length ; i += chunkSize ) {
73+ yield mapString . slice ( i , i + chunkSize ) ;
74+ }
75+ }
76+
77+ function processPersistentMode ( ) {
78+ const rl = readline . createInterface ( {
79+ input : process . stdin ,
80+ output : process . stdout ,
81+ terminal : false
82+ } ) ;
83+
84+ rl . on ( 'line' , ( line ) => {
85+ if ( line . trim ( ) ) {
86+ try {
87+ const request = JSON . parse ( line ) ;
88+
89+ if ( request . exit === true ) {
90+ rl . close ( ) ;
91+ process . exit ( 0 ) ;
92+ }
93+
94+ const response = compilePayload ( request ) ;
95+
96+ process . stdout . write ( JSON . stringify ( response ) + '\n' ) ;
97+ } catch ( err ) {
98+ sendError ( err . message ) ;
6699 }
67100 }
101+ } ) ;
68102
69- if ( options . loadPaths ) {
70- compileOpts . loadPaths = options . loadPaths ;
71- }
103+ rl . on ( 'close' , ( ) => {
104+ process . exit ( 0 ) ;
105+ } ) ;
106+ }
72107
73- if ( options . quietDeps ) {
74- compileOpts . quietDeps = options . quietDeps ;
75- }
108+ /**
109+ * Compiles a single payload and returns the response object
110+ */
111+ function compilePayload ( payload ) {
112+ const source = String ( payload . source || "" ) ;
113+ const options = payload . options || { } ;
114+ const url = payload . url ? new URL ( String ( payload . url ) ) : undefined ;
115+ const compileOpts = { } ;
76116
77- if ( options . silenceDeprecations ) {
78- compileOpts . silenceDeprecations = options . silenceDeprecations ;
79- }
117+ if ( url ) compileOpts . url = url ;
118+
119+ if ( options . syntax === 'sass' || options . syntax === 'indented' ) {
120+ compileOpts . syntax = 'indented' ;
121+ }
122+
123+ if ( options . minimize || ( 'compressed' in options && options . compressed ) || options . style === 'compressed' ) {
124+ compileOpts . style = 'compressed' ;
125+ }
126+
127+ const sourceMapPath = 'sourceMapPath' in options && options . sourceMapPath ? options . sourceMapPath : false ;
80128
81- if ( options . verbose ) {
82- compileOpts . verbose = options . verbose ;
129+ if ( options . sourceMap || sourceMapPath ) {
130+ compileOpts . sourceMap = sourceMapPath || options . sourceMap ;
131+
132+ if ( 'includeSources' in options && options . includeSources ) {
133+ compileOpts . sourceMapIncludeSources = options . includeSources ;
83134 }
135+ }
84136
85- const result = compileString ( source , compileOpts ) ;
137+ if ( options . loadPaths ) {
138+ compileOpts . loadPaths = options . loadPaths ;
139+ }
86140
87- const response = {
88- css : result . css ,
89- ...( result . sourceMap && { sourceMap : result . sourceMap } ) ,
90- } ;
141+ if ( options . quietDeps ) {
142+ compileOpts . quietDeps = options . quietDeps ;
143+ }
91144
92- // Check if streaming mode is requested for large results
93- if ( options . streamResult && result . css . length > 1024 * 1024 ) {
94- const cssChunks = Array . from ( cssChunkGenerator ( result . css ) ) ;
145+ if ( options . silenceDeprecations ) {
146+ compileOpts . silenceDeprecations = options . silenceDeprecations ;
147+ }
95148
96- response . chunks = cssChunks ;
97- response . isStreamed = true ;
149+ if ( options . verbose ) {
150+ compileOpts . verbose = options . verbose ;
151+ }
98152
99- delete response . css ;
100- }
153+ const result = compileString ( source , compileOpts ) ;
101154
102- process . stdout . write ( JSON . stringify ( response ) ) ;
103- } catch ( err ) {
104- process . stdout . write ( JSON . stringify ( { error : String ( err ?. message || err ) } ) ) ;
155+ const response = {
156+ css : result . css ,
157+ ...( result . sourceMap && { sourceMap : result . sourceMap } ) ,
158+ } ;
159+
160+ // Check if streaming mode is requested for large results
161+ if ( 'streamResult' in options && options . streamResult && result . css . length > 1024 * 1024 ) {
162+ response . chunks = Array . from ( cssChunkGenerator ( result . css ) ) ;
163+ response . isStreamed = true ;
164+
165+ delete response . css ;
105166 }
106- } ) ;
107- }
108167
109- /**
110- * Generator function for processing compilation results in chunks
111- * Useful for memory-efficient handling of large CSS outputs
112- */
113- function * cssChunkGenerator ( css , chunkSize = 64 * 1024 ) {
114- for ( let i = 0 ; i < css . length ; i += chunkSize ) {
115- yield css . slice ( i , i + chunkSize ) ;
168+ // Handle large sourceMap
169+ if ( response . sourceMap && JSON . stringify ( response . sourceMap ) . length > 1024 * 1024 ) {
170+ response . sourceMapChunks = Array . from ( sourceMapChunkGenerator ( response . sourceMap ) ) ;
171+ response . sourceMapIsStreamed = true ;
172+
173+ delete response . sourceMap ;
116174 }
175+
176+ return response ;
117177}
118178
119- function processPersistentMode ( ) {
120- // TODO: Implement persistent mode for multiple requests
121- process . exit ( 1 ) ;
179+ function sendError ( message ) {
180+ process . stdout . write ( JSON . stringify ( { error : message } ) + '\n' ) ;
122181}
0 commit comments