diff --git a/index.js b/index.js index 81df6d1..119b0ec 100644 --- a/index.js +++ b/index.js @@ -408,6 +408,29 @@ Cartero.prototype.processMains = function( callback ) { _this.packagePathsToIds[ newPackage.path ] = newPackage.id; + // calculates the shasum for the assets + _this.assetMap = _this.assetMap || {}; + async.each( assetTypesToWriteToDisk, function( thisAssetType, nextAssetType ) { + // We dont need to process assets that are in assetTypesToConcatenate + if ( _this.assetTypesToConcatenate.indexOf( thisAssetType ) !== -1 ) return nextAssetType(); + + async.each( newPackage.assetsByType[ thisAssetType ], function( thisAsset, nextAsset ) { + + var fileContent = fs.readFileSync( thisAsset.srcPath, 'utf-8' ); + var shasum = crypto.createHash( 'sha1' ); + + shasum.update(fileContent); + + var fileShasum =shasum.digest('hex'); + var fileName = path.relative( newPackage.path, thisAsset.srcPath ); + var fileExt = path.extname( fileName ); + var newFileName = path.basename( fileName, fileExt ) + '_' + fileShasum + fileExt; + + // save the old name and new name for later use with the transforms + _this.assetMap[ thisAsset.srcPath ] = newFileName; + }); + }); + newPackage.addTransform( replaceStringTransform, { find : /url\(\s*[\"\']?([^)\'\"]+)\s*[\"\']?\s*\)/g, replace : function( file, match, theUrl ) { @@ -417,12 +440,25 @@ Cartero.prototype.processMains = function( callback ) { if( theUrl.charAt( 0 ) === '/' ) return match; if( theUrl.indexOf( 'data:' ) === 0 ) return match; // data url, don't mess with this + var absoluteAssetPath = path.resolve( path.dirname( file ), theUrl ); + var newAssetName = _this.assetMap[ absoluteAssetPath ]; + + if ( newAssetName ) { + // replace the url with the new name + theUrl = path.join( path.dirname( theUrl ), newAssetName ); + } else { + // this happen when we have packages that have assets references that are not specified + // in the image tag in package.json. It happens in modules like jqueryui + log.warn( file, 'Asset reference ' + theUrl + ' cannot be resolved.' ); + } + var cssFilePathRelativeToPackageDir = path.relative( newPackage.path, file ); var cssFileDirPathRelativeToPackageDir = path.dirname( '/' + cssFilePathRelativeToPackageDir ); if( cssFileDirPathRelativeToPackageDir !== '/' ) cssFileDirPathRelativeToPackageDir += '/'; // urls in css files are relative to the css file itself var absUrl = url.resolve( cssFileDirPathRelativeToPackageDir, theUrl ); + absUrl = _this.outputDirUrl + newPackage.id + absUrl; return 'url( \'' + absUrl + '\' )'; @@ -431,7 +467,8 @@ Cartero.prototype.processMains = function( callback ) { newPackage.addTransform( assetUrlTransform, { packagePathsToIds : _this.packagePathsToIds, - outputDirUrl : _this.outputDirUrl + outputDirUrl : _this.outputDirUrl, + assetMap: _this.assetMap }, 'style' ); _this.writeIndividualAssetsToDisk( newPackage, assetTypesToWriteToDisk, function( err ) { @@ -515,7 +552,8 @@ Cartero.prototype.copyTempBundleToFinalDestination = function( tempBundlePath, a var postProcessorsToApply = _.clone( _this.postProcessors ); if( assetType === 'script' ) postProcessorsToApply.unshift( function( file ) { return assetUrlTransform( file, { packagePathsToIds : _this.packagePathsToIds, - outputDirUrl : _this.outputDirUrl + outputDirUrl : _this.outputDirUrl, + assetMap: _this.assetMap } ); } ); if( postProcessorsToApply.length !== 0 ) { @@ -683,17 +721,17 @@ Cartero.prototype.resolvePostProcessors = function( postProcessorNames, callback Cartero.prototype.writeIndividualAssetsToDisk = function( thePackage, assetTypesToWriteToDisk, callback ) { var _this = this; - var pathsOfWrittenAssets = []; var outputDirectoryPath = this.getPackageOutputDirectory( thePackage ); assetTypesToWriteToDisk = _.intersection( assetTypesToWriteToDisk, Object.keys( thePackage.assetsByType ) ); async.each( assetTypesToWriteToDisk, function( thisAssetType, nextAssetType ) { async.each( thePackage.assetsByType[ thisAssetType ], function( thisAsset, nextAsset ) { - var thisAssetDstPath = path.join( outputDirectoryPath, path.relative( thePackage.path, thisAsset.srcPath ) ); - if( thisAssetType === 'style' ) thisAssetDstPath = renameFileExtension( thisAssetDstPath, '.css' ); - pathsOfWrittenAssets.push( thisAssetDstPath ); + var thisAssetDstPath = path.relative( thePackage.path, thisAsset.srcPath ); + thisAssetDstPath = path.join( outputDirectoryPath, path.dirname( thisAssetDstPath ), _this.assetMap[ thisAsset.srcPath ] ); + + if( thisAssetType === 'style' ) thisAssetDstPath = renameFileExtension( thisAssetDstPath, '.css' ); thisAsset.writeToDisk( thisAssetDstPath, function( err ) { if( err ) return nextAsset( err ); @@ -760,7 +798,8 @@ Cartero.prototype.writeMetaDataFile = function( callback ) { var metaData = JSON.stringify( { formatVersion : 2, packageMap : packageMap, - entryPointMap : entryPointMap + entryPointMap : entryPointMap, + assetMap: _this.assetMap }, null, 4 ); fs.writeFile( metaDataFilePath, metaData, function( err ) { diff --git a/test/example4/node_modules/my-module/index.js b/test/example4/node_modules/my-module/index.js new file mode 100644 index 0000000..e69de29 diff --git a/test/example4/node_modules/my-module/package.json b/test/example4/node_modules/my-module/package.json new file mode 100644 index 0000000..e69de29 diff --git a/test/example4/node_modules/my-module/style.css b/test/example4/node_modules/my-module/style.css new file mode 100644 index 0000000..e69de29 diff --git a/test/example4/static/.gitignore b/test/example4/static/.gitignore new file mode 100644 index 0000000..ee88966 --- /dev/null +++ b/test/example4/static/.gitignore @@ -0,0 +1 @@ +assets/ diff --git a/test/example4/views/page1/img/robot.png b/test/example4/views/page1/img/robot.png new file mode 100644 index 0000000..e0d7324 --- /dev/null +++ b/test/example4/views/page1/img/robot.png @@ -0,0 +1 @@ +beepboop \ No newline at end of file diff --git a/test/example4/views/page1/package.json b/test/example4/views/page1/package.json new file mode 100644 index 0000000..3ca8d66 --- /dev/null +++ b/test/example4/views/page1/package.json @@ -0,0 +1,6 @@ +{ + "view" : "page1.jade", + "main" : "page1.js", + "style" : "*.css", + "image": "img/robot.png" +} \ No newline at end of file diff --git a/test/example4/views/page1/page1.css b/test/example4/views/page1/page1.css new file mode 100644 index 0000000..de46327 --- /dev/null +++ b/test/example4/views/page1/page1.css @@ -0,0 +1,3 @@ +body { + background : blue url('./img/robot.png'); +} \ No newline at end of file diff --git a/test/example4/views/page1/page1.jade b/test/example4/views/page1/page1.jade new file mode 100644 index 0000000..e69de29 diff --git a/test/example4/views/page1/page1.js b/test/example4/views/page1/page1.js new file mode 100644 index 0000000..81dcc61 --- /dev/null +++ b/test/example4/views/page1/page1.js @@ -0,0 +1,3 @@ +console.log( 'hellooo dave' ); + +var robotPngPath = '##asset_url( "./img/robot.png" )'; \ No newline at end of file diff --git a/test/test.js b/test/test.js index 6e8491b..9be132c 100644 --- a/test/test.js +++ b/test/test.js @@ -148,13 +148,13 @@ test( 'example3', function( t ) { page1JsBundle = path.join( outputDirPath, parcelIdsByPath[ 'page1' ], page1JsBundle ); var page1JsContents = fs.readFileSync( page1JsBundle, 'utf8' ); - t.ok( page1JsContents.indexOf( '/' + commonJsPackageId + '/robot.png' ) !== -1, '##asset_url resolved' ); - + t.ok( page1JsContents.indexOf( '/' + commonJsPackageId + '/robot_9018f21e83ce46f3ea2e3b73e5d75ece75407df7.png' ) !== -1, '##asset_url resolved' ); + var page1CssBundle = _.find( page1PackageFiles, function( thisFile ) { return path.extname( thisFile ) === '.css'; } ); page1CssBundle = path.join( outputDirPath, parcelIdsByPath[ 'page1' ], page1CssBundle ); var page1CssContents = fs.readFileSync( page1CssBundle, 'utf8' ); - t.ok( page1CssContents.indexOf( '/' + commonJsPackageId + '/robot.png' ) !== -1, 'relative css url resolved' ); + t.ok( page1CssContents.indexOf( '/' + commonJsPackageId + '/robot_9018f21e83ce46f3ea2e3b73e5d75ece75407df7.png' ) !== -1, 'relative css url resolved' ); t.ok( page1CssContents.indexOf( 'background : blue' ) !== -1, 'page 1 has correct background color' ); var page2CssBundle = _.find( page2PackageFiles, function( thisFile ) { return path.extname( thisFile ) === '.css'; } ); @@ -165,9 +165,57 @@ test( 'example3', function( t ) { t.ok( _.contains( fs.readdirSync( path.join( outputDirPath, commonJsPackageId ) ).sort(), - 'robot.png' + 'robot_9018f21e83ce46f3ea2e3b73e5d75ece75407df7.png' ), 'robot.png in common package' ); } ); } ); +test( 'example4', function( t ) { + t.plan( 5 ); + + var viewDirPath = path.join( __dirname, 'example4/views' ); + var outputDirPath = path.join( __dirname, 'example4/static/assets' ); + var packageId; + var packageMap = {}; + + var c = cartero( viewDirPath, outputDirPath, {} ); + + c.on( 'packageCreated', function( newPackage ) { + if( newPackage.isParcel ) { + packageId = newPackage.id; + } + + packageMap[ newPackage.path ] = newPackage.id; + } ); + + c.on( 'done', function() { + // Cannot determine the exact name o the files due to the fact that we calculate the shasum after some transformation of the code + // for more info check: https://github.com/rotundasoftware/cartero/pull/45 + var assets = require( path.join( outputDirPath, packageId, 'assets.json' ) ); + var cssFile = path.relative(packageId, assets.style[ 0 ]); + var jsFile = path.relative(packageId, assets.script[ 0 ]); + var imgDir = 'img'; + var imgFile = 'robot_9018f21e83ce46f3ea2e3b73e5d75ece75407df7.png'; + + t.ok( fs.existsSync( path.join( outputDirPath, packageId, imgDir, imgFile ) ), 'robot.png created with the new name' ); + + t.deepEqual( + fs.readdirSync( path.join( outputDirPath, packageId ) ).sort(), + [ 'assets.json', cssFile, jsFile, imgDir ].sort() + ); + + t.deepEqual( + fs.readdirSync( path.join( outputDirPath, packageId, 'img') ), + [ imgFile ] + ); + + // Test content of js file and check #asset_url + var jsContent = fs.readFileSync( path.join( outputDirPath, packageId, jsFile ), 'utf8' ); + t.notEqual( jsContent.indexOf( 'var robotPngPath = \'/' + [ packageId, imgDir, imgFile ].join( '/' ) + '\';' ), -1 ); + + t.equal( fs.readFileSync( path.join( outputDirPath, packageId, cssFile ), 'utf8' ), + 'body {\n\tbackground : blue url( \'/' + [ packageId, imgDir, imgFile ].join( '/' ) + '\' );\n}' ); + + } ); +} ); \ No newline at end of file diff --git a/transforms/asset_url.js b/transforms/asset_url.js index 24833ac..5076505 100644 --- a/transforms/asset_url.js +++ b/transforms/asset_url.js @@ -22,11 +22,16 @@ module.exports = function( file, options ) { var url = pathMapper( assetSrcAbsPath, function( srcDir ) { return options.packagePathsToIds[ srcDir ] ? '/' + options.packagePathsToIds[ srcDir ] : null; // return val of outputDirPath needs to be absolute path } ); - + // all assets urls should be different than their paths.. otherwise we have a problem if( url === assetSrcAbsPath ) return _this.emit( 'error', new Error( 'The file "' + assetSrcAbsPath + '" referenced from ##asset_url( "' + assetSrcPath + '" ) in file "' + file + '" is not an asset.' ) ); + var filename = path.basename(assetSrcAbsPath); + var newFilename = options.assetMap[assetSrcAbsPath]; + + url = path.join( path.dirname( url ), newFilename); + if( options.outputDirUrl ) { var baseUrl = options.outputDirUrl;