/ *
Useful system level functions for use in build commands . It ' s mostly things like
detecting if two files changed , doing globs on Windows and Unix , and various
file operations that you need .
* /
import fg from "fast-glob" ;
import fs from "fs" ;
import assert from "assert" ;
import { log } from "./logging.js" ;
import Path from "path" ;
import { execSync } from "child_process" ;
/ *
Fixes some common problems with ` fast-glob ` on windows . I removes
leading "C:\" from paths and replaces all of the ` \\ ` with ` / ` .
_ _BUG _ _ : This obviously won 't work if you' re on a different drive than C : .
+ ` path string ` -- The path / glob pattern to use .
* /
export const glob = ( path ) => {
if ( process . platform === "win32" ) {
// fast-glob doesn't understand \\ or C:\
const fixed _path = path . replace ( "C:" , "" ) . replaceAll ( '\\' , '/' )
return fg . sync ( fixed _path ) ;
} else {
return fg . sync ( path ) ;
}
}
/ *
Detects if two files have changed . This is used to avoid
writing or working on files that don ' t need it , similar
to the Unix make utility .
_ _ _FOOTGUN _ _ _ : It uses the stat . mtimeMs which may or may not be
the most reliable way to detect differences .
+ ` source string ` -- The source path .
+ ` target string ` -- The target path .
* /
export const changed = ( source , target ) => {
// we want the source to error if it doesn't exist
const s _stat = fs . statSync ( source ) ;
// but target might not exist, which would mean it's a change
const t _stat = fs . statSync ( target , { throwIfNoEntry : false } ) ;
return ! t _stat || s _stat . mtimeMs > t _stat . mtimeMs ;
}
/ *
Makes a directory recursively , kind of like ` mkdir -p ` .
+ ` dir string ` -- The full path to the final directory to create .
* /
export const mkdir = ( dir ) => {
if ( ! fs . existsSync ( dir ) ) {
log . debug ( ` making dir ${ dir } ` ) ;
fs . mkdirSync ( dir , { recursive : true } ) ;
}
}
/ *
Given any path to a file , makes sure all its directories are in place . This will
only the directory to the file , and then calls ` mkdir ` on it to create the path .
- ` param string ` -- path to file
* /
export const mkdir _to = ( target ) => {
let dirs = Path . parse ( target ) ;
mkdir ( dirs . dir ) ;
}
/ *
Convenience function that makes sure the dir is there then writes the data .
It ' s a combination of ` mkdir_to ` and ` fs.writeFileSync ` and exists because
these two operations are so common .
+ ` target string ` -- Path to the file to write .
+ ` data string|buffer|etc. ` -- Anything that ` fs.writeFileSync ` can take as file contents .
* /
export const write = ( target , data ) => {
mkdir _to ( target ) ;
fs . writeFileSync ( target , data ) ;
}
/ *
Like write but does an OS copy after making the target dir . When given a
filter it will give the filter function the arguments , let it return contents ,
then write those instead of a copy . This will create the target file ' s path
with ` mkdir_to ` .
+ ` src string ` -- Source file path .
+ ` dest string ` -- Destination path .
+ ` filter cb(src, dest, raw_data) ` -- A function that can modify the data before it ' s written .
* /
export const copy = ( src , dest , filter = null ) => {
mkdir _to ( dest ) ;
if ( filter ) {
try {
let raw _data = fs . readFileSync ( src ) ;
let data = filter ( src , dest , raw _data ) ;
fs . writeFileSync ( dest , data ) ;
} catch ( error ) {
log . error ( error , "Problem with filter write in copy" ) ;
}
} else {
fs . copyFileSync ( src , dest ) ;
}
}
/ *
Removes a file , optionally ignoring any errors .
+ ` file_name string ` -- The path to the file to remove .
+ ` ignore boolean (false) ` -- Whether to ignore errors removing the file .
* /
export const rm = ( file _name , ignore = false ) => {
log . warn ( "Deleting file" , file _name ) ;
try {
fs . unlinkSync ( file _name ) ;
} catch ( error ) {
if ( ignore ) {
log . debug ( ` Requested delete file ${ file _name } doesn't exist, but ignored. ` ) ;
} else {
log . error ( error , file _name ) ;
throw error ;
}
}
}
/ *
Executes a command , and mostly just logs the command then run
execSync with the same options .
+ ` cmd string ` -- Command to run .
+ ` opts Object ` -- Options for ` execSync ` .
* /
export const exec = ( cmd , opts ) => {
log . info ( ` EXEC ${ cmd } ` ) ;
return execSync ( cmd , opts ) ;
}
/ *
Similar to ` exec ` but it is _interactive _ because it reroutes this process ' s stderr ,
stdout , and stdin to the process . If you find you can ' t ctrl - c then use this . The
options set are :
` ` `
{ stdio : [ process . stdout , process . stderr , process . stdin ] }
` ` `
+ ` cmd string ` -- The command to run .
+ ` opts Object ` -- Additional options to set for the exec command .
* /
export const exec _i = ( cmd , opts = { } ) => {
return exec ( cmd , { stdio : [ process . stdout , process . stderr , process . stdin ] , ... opts } ) ;
}
export default {
rm , exec , changed , mkdir , mkdir _to , write , copy , exec _i , glob
}