@@ -79,14 +79,8 @@ class CSSSource {
7979 }
8080
8181 public static fromURI ( uri : string , keyframes : KeyframesMap ) : CSSSource {
82- // webpack modules require all file paths to be relative to /app folder.
83- let appRelativeUri = uri ;
84- if ( appRelativeUri . startsWith ( "/" ) ) {
85- var app = knownFolders . currentApp ( ) . path + "/" ;
86- if ( appRelativeUri . startsWith ( app ) ) {
87- appRelativeUri = "./" + appRelativeUri . substr ( app . length ) ;
88- }
89- }
82+ // webpack modules require all file paths to be relative to /app folder
83+ let appRelativeUri = CSSSource . pathRelativeToApp ( uri ) ;
9084
9185 try {
9286 const cssOrAst = global . loadModule ( appRelativeUri ) ;
@@ -109,6 +103,21 @@ class CSSSource {
109103 return CSSSource . fromFile ( appRelativeUri , keyframes ) ;
110104 }
111105
106+ private static pathRelativeToApp ( uri : string ) : string {
107+ if ( ! uri . startsWith ( "/" ) ) {
108+ return uri ;
109+ }
110+
111+ const appPath = knownFolders . currentApp ( ) . path ;
112+ if ( ! uri . startsWith ( appPath ) ) {
113+ traceWrite ( `${ uri } does not start with ${ appPath } ` , traceCategories . Error , traceMessageType . error ) ;
114+ return uri ;
115+ }
116+
117+ const relativeUri = `.${ uri . substr ( appPath . length ) } ` ;
118+ return relativeUri ;
119+ }
120+
112121 public static fromFile ( url : string , keyframes : KeyframesMap ) : CSSSource {
113122 // .scss, .sass, etc. css files in vanilla app are usually compiled to .css so we will try to load a compiled file first.
114123 let cssFileUrl = url . replace ( / \. .\w + $ / , ".css" ) ;
@@ -123,10 +132,15 @@ class CSSSource {
123132 return new CSSSource ( undefined , url , file , keyframes , undefined ) ;
124133 }
125134
135+ public static fromFileImport ( url : string , keyframes : KeyframesMap , importSource : string ) : CSSSource {
136+ const file = CSSSource . resolveCSSPathFromURL ( url , importSource ) ;
137+ return new CSSSource ( undefined , url , file , keyframes , undefined ) ;
138+ }
139+
126140 @profile
127- public static resolveCSSPathFromURL ( url : string ) : string {
141+ public static resolveCSSPathFromURL ( url : string , importSource ?: string ) : string {
128142 const app = knownFolders . currentApp ( ) . path ;
129- const file = resolveFileNameFromUrl ( url , app , File . exists ) ;
143+ const file = resolveFileNameFromUrl ( url , app , File . exists , importSource ) ;
130144 return file ;
131145 }
132146
@@ -197,21 +211,33 @@ class CSSSource {
197211 }
198212
199213 private createSelectorsFromImports ( ) : RuleSet [ ] {
200- let selectors : RuleSet [ ] = [ ] ;
201214 const imports = this . _ast [ "stylesheet" ] [ "rules" ] . filter ( r => r . type === "import" ) ;
202- for ( let i = 0 ; i < imports . length ; i ++ ) {
203- const importItem = imports [ i ] [ "import" ] ;
204215
205- const match = importItem && ( < string > importItem ) . match ( pattern ) ;
206- const url = match && match [ 2 ] ;
216+ const urlFromImportObject = importObject => {
217+ const importItem = importObject [ "import" ] as string ;
218+ const urlMatch = importItem && importItem . match ( pattern ) ;
219+ return urlMatch && urlMatch [ 2 ] ;
220+ } ;
207221
208- if ( url !== null && url !== undefined ) {
209- const cssFile = CSSSource . fromURI ( url , this . _keyframes ) ;
210- selectors = selectors . concat ( cssFile . selectors ) ;
211- }
212- }
222+ const sourceFromImportObject = importObject =>
223+ importObject [ "position" ] && importObject [ "position" ] [ "source" ] ;
224+
225+ const toUrlSourcePair = importObject => ( {
226+ url : urlFromImportObject ( importObject ) ,
227+ source : sourceFromImportObject ( importObject ) ,
228+ } ) ;
213229
214- return selectors ;
230+ const getCssFile = ( { url, source } ) => source ?
231+ CSSSource . fromFileImport ( url , this . _keyframes , source ) :
232+ CSSSource . fromURI ( url , this . _keyframes ) ;
233+
234+ const cssFiles = imports
235+ . map ( toUrlSourcePair )
236+ . filter ( ( { url } ) => ! ! url )
237+ . map ( getCssFile ) ;
238+
239+ const selectors = cssFiles . map ( file => ( file && file . selectors ) || [ ] ) ;
240+ return selectors . reduce ( ( acc , val ) => acc . concat ( val ) , [ ] ) ;
215241 }
216242
217243 private createSelectorsFromSyntaxTree ( ) : RuleSet [ ] {
@@ -521,13 +547,13 @@ export class StyleScope {
521547
522548 let parsedCssSelectors = cssString ? CSSSource . fromSource ( cssString , this . _keyframes , cssFileName ) : CSSSource . fromURI ( cssFileName , this . _keyframes ) ;
523549 this . _css = this . _css + parsedCssSelectors . source ;
524- this . _localCssSelectors . push . apply ( this . _localCssSelectors , parsedCssSelectors . selectors ) ;
550+ this . _localCssSelectors . push . apply ( this . _localCssSelectors , parsedCssSelectors . selectors ) ;
525551 this . _localCssSelectorVersion ++ ;
526552 this . ensureSelectors ( ) ;
527553 }
528554
529555 public getKeyframeAnimationWithName ( animationName : string ) : kam . KeyframeAnimationInfo {
530- const cssKeyframes = this . _keyframes [ animationName ] ;
556+ const cssKeyframes = this . _keyframes [ animationName ] ;
531557 if ( ! cssKeyframes ) {
532558 return ;
533559 }
@@ -611,9 +637,9 @@ export class StyleScope {
611637
612638type KeyframesMap = Map < string , Keyframes > ;
613639
614- export function resolveFileNameFromUrl ( url : string , appDirectory : string , fileExists : ( name : string ) => boolean ) : string {
615- let fileName : string = typeof url === "string" ? url . trim ( ) : "" ;
640+ export function resolveFileNameFromUrl ( url : string , appDirectory : string , fileExists : ( name : string ) => boolean , importSource ?: string ) : string {
616641
642+ let fileName : string = typeof url === "string" ? url . trim ( ) : "" ;
617643 if ( fileName . indexOf ( "~/" ) === 0 ) {
618644 fileName = fileName . replace ( "~/" , "" ) ;
619645 }
@@ -628,6 +654,14 @@ export function resolveFileNameFromUrl(url: string, appDirectory: string, fileEx
628654 if ( fileName [ 0 ] === "~" && fileName [ 1 ] !== "/" && fileName [ 1 ] !== "\"" ) {
629655 fileName = fileName . substr ( 1 ) ;
630656 }
657+
658+ if ( importSource ) {
659+ const importFile = resolveFilePathFromImport ( importSource , fileName ) ;
660+ if ( fileExists ( importFile ) ) {
661+ return importFile ;
662+ }
663+ }
664+
631665 const external = path . join ( appDirectory , "tns_modules" , fileName ) ;
632666 if ( fileExists ( external ) ) {
633667 return external ;
@@ -637,6 +671,19 @@ export function resolveFileNameFromUrl(url: string, appDirectory: string, fileEx
637671 return null ;
638672}
639673
674+ function resolveFilePathFromImport ( importSource : string , fileName : string ) : string {
675+ const importSourceParts = importSource . split ( path . separator ) ;
676+ const fileNameParts = fileName . split ( path . separator )
677+ // exclude the dot-segment for current directory
678+ . filter ( p => ! isCurrentDirectory ( p ) ) ;
679+
680+ // remove current file name
681+ importSourceParts . pop ( ) ;
682+ // remove element in case of dot-segment for parent directory or add file name
683+ fileNameParts . forEach ( p => isParentDirectory ( p ) ? importSourceParts . pop ( ) : importSourceParts . push ( p ) ) ;
684+ return importSourceParts . join ( path . separator ) ;
685+ }
686+
640687export const applyInlineStyle = profile ( function applyInlineStyle ( view : ViewBase , styleStr : string ) {
641688 let localStyle = `local { ${ styleStr } }` ;
642689 let inlineRuleSet = CSSSource . fromSource ( localStyle , new Map ( ) ) . selectors ;
@@ -657,6 +704,14 @@ export const applyInlineStyle = profile(function applyInlineStyle(view: ViewBase
657704 } ) ;
658705} ) ;
659706
707+ function isCurrentDirectory ( uriPart : string ) : boolean {
708+ return uriPart === "." ;
709+ }
710+
711+ function isParentDirectory ( uriPart : string ) : boolean {
712+ return uriPart === ".." ;
713+ }
714+
660715function isKeyframe ( node : CssNode ) : node is KeyframesDefinition {
661716 return node . type === "keyframes" ;
662717}
0 commit comments