@@ -194,7 +194,8 @@ exports.main = function main(argv, options, callback) {
194194 if ( ! stderr ) throw Error ( "'options.stderr' must be specified" ) ;
195195
196196 const opts = optionsUtil . parse ( argv , exports . options ) ;
197- const args = opts . options ;
197+ let args = opts . options ;
198+
198199 argv = opts . arguments ;
199200 if ( args . noColors ) {
200201 colorsUtil . stdout . supported =
@@ -270,6 +271,90 @@ exports.main = function main(argv, options, callback) {
270271
271272 // Set up base directory
272273 const baseDir = args . baseDir ? path . resolve ( args . baseDir ) : "." ;
274+ const target = args . target ;
275+
276+ // Once the baseDir is calculated, we can resolve the config, and its extensions
277+ let asconfig = getAsconfig ( args . config , baseDir , readFile ) ;
278+ let asconfigDir = baseDir ;
279+
280+ const seenAsconfig = new Set ( ) ;
281+ seenAsconfig . add ( path . join ( baseDir , args . config ) ) ;
282+
283+ while ( asconfig ) {
284+ // merge target first, then merge options, then merge extended asconfigs
285+ if ( asconfig . targets && asconfig . targets [ target ] ) {
286+ args = optionsUtil . merge ( exports . options , asconfig . targets [ target ] , args ) ;
287+ }
288+ if ( asconfig . options ) {
289+ if ( asconfig . options . transform ) {
290+ // ensure that a transform's path is relative to the current config
291+ asconfig . options . transform = asconfig . options . transform . map ( p => {
292+ if ( ! path . isAbsolute ( p ) ) {
293+ if ( p . startsWith ( "." ) ) {
294+ return path . join ( asconfigDir , p ) ;
295+ }
296+ return require . resolve ( p ) ;
297+ }
298+ return p ;
299+ } ) ;
300+ }
301+ args = optionsUtil . merge ( exports . options , args , asconfig . options ) ;
302+ }
303+
304+ // entries are added to the compilation
305+ if ( asconfig . entries ) {
306+ for ( const entry of asconfig . entries ) {
307+ argv . push (
308+ path . isAbsolute ( entry )
309+ ? entry
310+ // the entry is relative to the asconfig directory
311+ : path . join ( asconfigDir , entry )
312+ ) ;
313+ }
314+ }
315+
316+ // asconfig "extends" another config, merging options of it's parent
317+ if ( asconfig . extends ) {
318+ asconfigDir = path . isAbsolute ( asconfig . extends )
319+ // absolute extension path means we know the exact directory and location
320+ ? path . dirname ( asconfig . extends )
321+ // relative means we need to calculate a relative asconfigDir
322+ : path . join ( asconfigDir , path . dirname ( asconfig . extends ) ) ;
323+ const fileName = path . basename ( asconfig . extends ) ;
324+ const filePath = path . join ( asconfigDir , fileName ) ;
325+ if ( seenAsconfig . has ( filePath ) ) {
326+ asconfig = null ;
327+ } else {
328+ seenAsconfig . add ( filePath ) ;
329+ asconfig = getAsconfig ( fileName , asconfigDir , readFile ) ;
330+ }
331+ } else {
332+ asconfig = null ; // finished resolving the configuration chain
333+ }
334+ }
335+
336+ // If showConfig print args and exit
337+ if ( args . showConfig ) {
338+ stderr . write ( JSON . stringify ( args , null , 2 ) ) ;
339+ return callback ( null ) ;
340+ }
341+
342+ // This method resolves a path relative to the baseDir instead of process.cwd()
343+ function resolveBasedir ( arg ) {
344+ return path . resolve ( baseDir , arg ) ;
345+ }
346+
347+ // create a unique set of values
348+ function unique ( values ) {
349+ return [ ...new Set ( values ) ] ;
350+ }
351+
352+ // returns a relative path from baseDir
353+ function makeRelative ( arg ) {
354+ return path . relative ( baseDir , arg ) ;
355+ }
356+ // postprocess we need to get absolute file locations argv
357+ argv = unique ( argv . map ( resolveBasedir ) ) . map ( makeRelative ) ;
273358
274359 // Set up options
275360 const compilerOptions = assemblyscript . newOptions ( ) ;
@@ -347,7 +432,7 @@ exports.main = function main(argv, options, callback) {
347432 const transforms = [ ] ;
348433 if ( args . transform ) {
349434 let tsNodeRegistered = false ;
350- let transformArgs = args . transform ;
435+ let transformArgs = unique ( args . transform . map ( resolveBasedir ) ) ;
351436 for ( let i = 0 , k = transformArgs . length ; i < k ; ++ i ) {
352437 let filename = transformArgs [ i ] . trim ( ) ;
353438 if ( ! tsNodeRegistered && filename . endsWith ( ".ts" ) ) { // ts-node requires .ts specifically
@@ -376,6 +461,7 @@ exports.main = function main(argv, options, callback) {
376461 }
377462 }
378463 }
464+
379465 function applyTransform ( name , ...args ) {
380466 for ( let i = 0 , k = transforms . length ; i < k ; ++ i ) {
381467 let transform = transforms [ i ] ;
@@ -400,11 +486,12 @@ exports.main = function main(argv, options, callback) {
400486 assemblyscript . parse ( program , exports . libraryFiles [ libPath ] , exports . libraryPrefix + libPath + extension . ext , false ) ;
401487 } ) ;
402488 } ) ;
403- const customLibDirs = [ ] ;
489+ let customLibDirs = [ ] ;
404490 if ( args . lib ) {
405491 let lib = args . lib ;
406- if ( typeof lib === "string" ) lib = lib . split ( "," ) ;
407- Array . prototype . push . apply ( customLibDirs , lib . map ( lib => lib . trim ( ) ) ) ;
492+ if ( typeof lib === "string" ) lib = lib . trim ( ) . split ( / \s * , \s * / ) ;
493+ customLibDirs . push ( ...lib . map ( resolveBasedir ) ) ;
494+ customLibDirs = unique ( customLibDirs ) ; // `lib` and `customLibDirs` may include duplicates
408495 for ( let i = 0 , k = customLibDirs . length ; i < k ; ++ i ) { // custom
409496 let libDir = customLibDirs [ i ] ;
410497 let libFiles ;
@@ -574,7 +661,7 @@ exports.main = function main(argv, options, callback) {
574661 const filename = argv [ i ] ;
575662
576663 let sourcePath = String ( filename ) . replace ( / \\ / g, "/" ) . replace ( extension . re , "" ) . replace ( / [ \\ / ] $ / , "" ) ;
577-
664+
578665 // Setting the path to relative path
579666 sourcePath = path . isAbsolute ( sourcePath ) ? path . relative ( baseDir , sourcePath ) : sourcePath ;
580667
@@ -925,6 +1012,56 @@ exports.main = function main(argv, options, callback) {
9251012 }
9261013} ;
9271014
1015+ const toString = Object . prototype . toString ;
1016+
1017+ function isObject ( arg ) {
1018+ return toString . call ( arg ) === "[object Object]" ;
1019+ }
1020+
1021+ function getAsconfig ( file , baseDir , readFile ) {
1022+ const contents = readFile ( file , baseDir ) ;
1023+ const location = path . join ( baseDir , file ) ;
1024+ if ( ! contents ) return null ;
1025+
1026+ // obtain the configuration
1027+ let config ;
1028+ try {
1029+ config = JSON . parse ( contents ) ;
1030+ } catch ( ex ) {
1031+ throw new Error ( "Asconfig is not valid json: " + location ) ;
1032+ }
1033+
1034+ // validate asconfig shape
1035+ if ( config . options && ! isObject ( config . options ) ) {
1036+ throw new Error ( "Asconfig.options is not an object: " + location ) ;
1037+ }
1038+
1039+ if ( config . include && ! Array . isArray ( config . include ) ) {
1040+ throw new Error ( "Asconfig.include is not an array: " + location ) ;
1041+ }
1042+
1043+ if ( config . targets ) {
1044+ if ( ! isObject ( config . targets ) ) {
1045+ throw new Error ( "Asconfig.targets is not an object: " + location ) ;
1046+ }
1047+ const targets = Object . keys ( config . targets ) ;
1048+ for ( let i = 0 ; i < targets . length ; i ++ ) {
1049+ const target = targets [ i ] ;
1050+ if ( ! isObject ( config . targets [ target ] ) ) {
1051+ throw new Error ( "Asconfig.targets." + target + " is not an object: " + location ) ;
1052+ }
1053+ }
1054+ }
1055+
1056+ if ( config . extends && typeof config . extends !== "string" ) {
1057+ throw new Error ( "Asconfig.extends is not a string: " + location ) ;
1058+ }
1059+
1060+ return config ;
1061+ }
1062+
1063+ exports . getAsconfig = getAsconfig ;
1064+
9281065/** Checks diagnostics emitted so far for errors. */
9291066function checkDiagnostics ( program , stderr ) {
9301067 var diagnostic ;
0 commit comments