1+ import path from 'path' ;
12import OriginalSource from 'webpack-sources/lib/OriginalSource' ;
23import RawSource from 'webpack-sources/lib/RawSource' ;
34import ReplaceSource from 'webpack-sources/lib/ReplaceSource' ;
45import SourceMapSource from 'webpack-sources/lib/SourceMapSource' ;
56import loaderUtils from 'loader-utils' ;
67import parse from './parse' ;
78
9+ function toRelativeUrl ( p , context ) {
10+ const url = path . relative ( context , p ) . replace ( / \\ / g, '/' ) ;
11+ if ( / ^ \. \. ? \/ / . test ( url ) ) {
12+ return url ;
13+ }
14+ return `./${ url } ` ;
15+ }
16+
817export default function loader ( source , map ) {
918 const options = loaderUtils . getOptions ( this ) || { } ;
1019 const remainingRequest = loaderUtils . getRemainingRequest ( this ) ;
@@ -19,9 +28,11 @@ export default function loader(source, map) {
1928 replacer = new ReplaceSource ( new RawSource ( source ) , remainingRequest ) ;
2029 }
2130
31+ // List of @imports with media queries
2232 const includedStylesheets = new Set ( ) ;
2333 const includedStylesheetsMediaQuery = new Map ( ) ;
2434
35+ // Interate parsed @import
2536 parseResult . atImports . forEach ( ( imp ) => {
2637 if ( loaderUtils . isUrlRequest ( imp . url , options . root ) ) {
2738 const request = loaderUtils . urlToRequest ( imp . url , options . root ) ;
@@ -31,17 +42,29 @@ export default function loader(source, map) {
3142 }
3243 } ) ;
3344
45+ // Flag if column mappings make sense for this SourceMap
46+ // It only makes sense if we don't replace values from imported values
3447 let columns = true ;
48+
49+ // Mapping from css-identifier to import
3550 const importedNames = new Map ( ) ;
3651
52+ // Interate parsed :import
3753 parseResult . imports . forEach ( ( imp ) => {
3854 importedNames . set ( imp . alias , imp ) ;
3955 } ) ;
4056
57+ // List of all declarates that should be emitted to the output
4158 const declarations = [ ] ;
59+
60+ // Mapping from css-identifier to imported identifier
4261 const importReplacements = new Map ( ) ;
4362
63+ // Counter to generate unique ids for identifiers
4464 let id = 0 ;
65+
66+ // Iterate all imported names and internal identifiers
67+ // Also make sure that :imports are imported like @imports
4568 for ( const pair of importedNames ) {
4669 const internalName = `cssLoaderImport${ id } _${ pair [ 1 ] . importName } ` ;
4770 id += 1 ;
@@ -50,6 +73,7 @@ export default function loader(source, map) {
5073 includedStylesheets . add ( pair [ 1 ] . from ) ;
5174 }
5275
76+ // Iterate all replacements and replace them with a maker token
5377 for ( const pair of importReplacements ) {
5478 const identifier = parseResult . identifiers . get ( pair [ 0 ] ) ;
5579 if ( identifier ) {
@@ -61,10 +85,12 @@ export default function loader(source, map) {
6185 }
6286 }
6387
88+ // Delete all metablocks, they only contain meta information and are not valid css
6489 parseResult . metablocks . forEach ( ( block ) => {
6590 replacer . replace ( block . start , block . end , '' ) ;
6691 } ) ;
6792
93+ // Generate declarations for all imports
6894 const includedStylesheetsArray = [ ] ;
6995 for ( const include of includedStylesheets ) {
7096 const internalName = `cssLoaderImport${ id } ` ;
@@ -76,14 +102,62 @@ export default function loader(source, map) {
76102 } ) ;
77103 }
78104
105+ // Mapping from exported name to exported value as array (will be joined by spaces)
106+ const exportedNames = new Map ( ) ;
107+
108+ // Iterate parsed exports
109+ parseResult . exports . forEach ( ( exp ) => {
110+ // Note this elimiate duplicates, only last exported value is valid
111+ exportedNames . set ( exp . name , exp . value ) ;
112+ } ) ;
113+
114+ for ( const pair of exportedNames ) {
115+ const [ name , value ] = pair ;
116+ const processedValues = value . map ( ( item ) => {
117+ const replacement = importReplacements . get ( item ) ;
118+ if ( replacement ) {
119+ return {
120+ name : replacement ,
121+ } ;
122+ }
123+ return item ;
124+ } ) . reduce ( ( arr , item ) => {
125+ if ( typeof item === 'string' && arr . length > 0 && typeof arr [ arr . length - 1 ] === 'string' ) {
126+ arr [ arr . length - 1 ] += item ; // eslint-disable-line no-param-reassign
127+ return arr ;
128+ }
129+ arr . push ( item ) ;
130+ return arr ;
131+ } , [ ] ) ;
132+ if ( processedValues . length === 1 && typeof processedValues [ 0 ] !== 'string' ) {
133+ declarations . push ( `export { ${ processedValues [ 0 ] . name } as ${ name } };` ) ;
134+ } else {
135+ const valuesJs = processedValues . map ( ( item ) => {
136+ if ( typeof item === 'string' ) {
137+ return JSON . stringify ( item ) ;
138+ }
139+ return item . name ;
140+ } ) . join ( ' + ' ) ;
141+ declarations . push ( `export var ${ name } = ${ valuesJs } ;` ) ;
142+ }
143+ }
144+
79145 let css ;
80146 let sourceMap ;
81147 if ( options . sourceMap ) {
82148 const sourceAndMap = replacer . sourceAndMap ( typeof options . sourceMap === 'object' ? options . sourceMap : {
83149 columns,
84150 } ) ;
85- css = sourceAndMap . code ;
151+ css = sourceAndMap . source ;
86152 sourceMap = sourceAndMap . map ;
153+ if ( options . sourceMapContext ) {
154+ sourceMap . sources = sourceMap . sources . map ( absPath => toRelativeUrl ( absPath , options . sourceMapContext ) ) ;
155+ }
156+ if ( options . sourceMapPrefix ) {
157+ sourceMap . sources = sourceMap . sources . map ( sourcePath => options . sourceMapPrefix + sourcePath ) ;
158+ } else {
159+ sourceMap . sources = sourceMap . sources . map ( sourcePath => `webpack-css:///${ sourcePath } ` ) ;
160+ }
87161 } else {
88162 css = replacer . source ( ) ;
89163 sourceMap = null ;
@@ -94,19 +168,17 @@ export default function loader(source, map) {
94168 return [
95169 '// css runtime' ,
96170 `import * as runtime from ${ loaderUtils . stringifyRequest ( this , require . resolve ( '../runtime' ) ) } ;` ,
97- '' ,
98- '// declarations' ,
99171 declarations . join ( '\n' ) ,
100172 '' ,
101173 '// CSS' ,
102174 'export default /*#__PURE__*/runtime.create([' ,
103175 ] . concat (
104176 includedStylesheetsArray . map ( ( include ) => {
105177 if ( ! include . mediaQuery ) return ` ${ include . name } ,` ;
106- return ` runtime.importStylesheet(${ include . name } , ${ JSON . stringify ( include . mediaQuery ) } ,` ;
178+ return ` runtime.importStylesheet(${ include . name } , ${ JSON . stringify ( include . mediaQuery ) } ) ,` ;
107179 } ) ,
108180 ) . concat ( [
109- ` runtime.${ sourceMap ? 'moduleWithSourceMap' : 'moduleWithoutSourceMap' } (module.id, ${ cssJs } ${ sourceMap ? `, ${ sourceMap } ` : '' } )` ,
181+ ` runtime.${ sourceMap ? 'moduleWithSourceMap' : 'moduleWithoutSourceMap' } (module.id, ${ cssJs } ${ sourceMap ? `, ${ JSON . stringify ( sourceMap ) } ` : '' } )` ,
110182 ']);' ,
111183 ] ) . join ( '\n' ) ;
112184}
0 commit comments