2016-08-29 00:04:48 +02:00
// Copyright 2016 Google Inc. Use of this source code is governed by an
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
2016-10-15 00:48:34 +02:00
import ' dart:async ' ;
import ' dart:convert ' ;
2016-10-05 10:04:51 +02:00
import ' dart:io ' ;
2016-10-15 00:48:34 +02:00
import ' package:archive/archive.dart ' ;
2016-08-29 00:04:48 +02:00
import ' package:grinder/grinder.dart ' ;
2016-10-15 00:48:34 +02:00
import ' package:http/http.dart ' as http ;
2016-10-05 10:04:51 +02:00
import ' package:node_preamble/preamble.dart ' as preamble ;
2016-10-15 00:48:34 +02:00
import ' package:path/path.dart ' as p ;
2017-01-14 01:13:26 +01:00
import ' package:xml/xml.dart ' as xml ;
2016-10-08 00:46:58 +02:00
import ' package:yaml/yaml.dart ' ;
2016-08-29 00:04:48 +02:00
2016-10-15 00:48:34 +02:00
/// The version of Dart Sass.
final String _version =
2017-01-08 23:31:26 +01:00
loadYaml ( new File ( ' pubspec.yaml ' ) . readAsStringSync ( ) ) [ ' version ' ] as String ;
2016-10-15 00:48:34 +02:00
/// The version of the current Dart executable.
final String _dartVersion = Platform . version . split ( " " ) . first ;
/// The root of the Dart SDK.
final _sdkDir = p . dirname ( p . dirname ( Platform . resolvedExecutable ) ) ;
2017-01-08 23:31:26 +01:00
main ( List < String > args ) = > grind ( args ) ;
2016-08-29 00:04:48 +02:00
2016-10-05 10:04:51 +02:00
@ DefaultTask ( ' Run the Dart formatter. ' )
2016-08-29 00:04:48 +02:00
format ( ) {
Pub . run ( ' dart_style ' ,
script: ' format ' ,
arguments: [ ' --overwrite ' ]
. . addAll ( existingSourceDirs . map ( ( dir ) = > dir . path ) ) ) ;
}
2016-10-05 10:04:51 +02:00
@ Task ( ' Build Dart snapshot. ' )
snapshot ( ) {
_ensureBuild ( ) ;
Dart . run ( ' bin/sass.dart ' , vmArgs: [ ' --snapshot=build/sass.dart.snapshot ' ] ) ;
}
2016-10-15 02:33:51 +02:00
@ Task ( ' Build standalone packages for all OSes. ' )
2016-10-15 00:48:34 +02:00
@ Depends ( snapshot )
package ( ) async {
var client = new http . Client ( ) ;
await _buildPackage ( client , " linux " , " x64 " ) ;
await _buildPackage ( client , " linux " , " ia32 " ) ;
await _buildPackage ( client , " macos " , " x64 " ) ;
await _buildPackage ( client , " macos " , " ia32 " ) ;
await _buildPackage ( client , " windows " , " x64 " ) ;
await _buildPackage ( client , " windows " , " ia32 " ) ;
client . close ( ) ;
}
2016-10-05 10:04:51 +02:00
@ Task ( ' Compile to JS. ' )
js ( ) {
_ensureBuild ( ) ;
var destination = new File ( ' build/sass.dart.js ' ) ;
2016-10-15 02:38:50 +02:00
Dart2js . compile ( new File ( ' bin/sass.dart ' ) , outFile: destination , extraArgs: [
' -Dnode=true ' ,
2016-10-29 20:14:16 +02:00
' -Dversion= $ _version ' ,
' -Ddart-version= $ _dartVersion ' ,
2016-10-15 02:38:50 +02:00
] ) ;
2016-10-05 10:04:51 +02:00
var text = destination . readAsStringSync ( ) ;
2016-12-02 23:22:06 +01:00
destination . writeAsStringSync ( preamble . getPreamble ( ) + text ) ;
2016-10-15 02:33:51 +02:00
}
2016-10-25 01:45:51 +02:00
@ Task ( ' Build a pure-JS npm package. ' )
2016-10-15 02:33:51 +02:00
@ Depends ( js )
npm_package ( ) {
var dir = new Directory ( ' build/npm ' ) ;
2016-10-31 21:25:05 +01:00
if ( dir . existsSync ( ) ) dir . deleteSync ( recursive: true ) ;
2016-10-15 02:33:51 +02:00
dir . createSync ( recursive: true ) ;
log ( " copying package/package.json to build/npm " ) ;
2016-10-15 03:24:50 +02:00
var json = JSON . decode ( new File ( ' package/package.json ' ) . readAsStringSync ( ) ) ;
2016-10-15 02:33:51 +02:00
json [ ' version ' ] = _version ;
new File ( p . join ( dir . path , ' package.json ' ) )
. writeAsStringSync ( JSON . encode ( json ) ) ;
2017-02-11 00:24:56 +01:00
copy ( new File ( p . join ( ' package ' , ' sass.js ' ) ) , dir ) ;
copy ( new File ( p . join ( ' build ' , ' sass.dart.js ' ) ) , dir ) ;
2017-01-07 23:17:06 +01:00
copy ( new File ( ' README.md ' ) , dir ) ;
2016-10-05 10:04:51 +02:00
}
2017-01-14 01:13:26 +01:00
@ Task ( ' Build a Chocolatey package. ' )
@ Depends ( snapshot )
chocolatey_package ( ) {
_ensureBuild ( ) ;
var nuspec = _nuspec ( ) ;
var archive = new Archive ( )
. . addFile ( _fileFromString ( " sass.nuspec " , nuspec . toString ( ) ) )
. . addFile (
_file ( " [Content_Types].xml " , " package/chocolatey/[Content_Types].xml " ) )
. . addFile ( _file ( " _rels/.rels " , " package/chocolatey/rels.xml " ) )
. . addFile ( _fileFromString (
" package/services/metadata/core-properties/properties.psmdcp " ,
_nupkgProperties ( nuspec ) ) )
. . addFile ( _file ( " tools/LICENSE " , " LICENSE " ) )
. . addFile ( _file ( " tools/sass.dart.snapshot " , " build/sass.dart.snapshot " ) )
. . addFile ( _file ( " tools/chocolateyInstall.ps1 " ,
" package/chocolatey/chocolateyInstall.ps1 " ) )
. . addFile ( _file ( " tools/chocolateyUninstall.ps1 " ,
" package/chocolatey/chocolateyUninstall.ps1 " ) )
. . addFile ( _fileFromString ( " tools/sass.bat " ,
_readAndReplaceVersion ( " package/chocolatey/sass.bat " ) ) ) ;
var output = " build/sass. ${ _chocolateyVersion ( ) } .nupkg " ;
log ( " Creating $ output ... " ) ;
new File ( output ) . writeAsBytesSync ( new ZipEncoder ( ) . encode ( archive ) ) ;
}
/// Creates a `sass.nuspec` file's contents.
xml . XmlDocument _nuspec ( ) {
var builder = new xml . XmlBuilder ( ) ;
builder . processing ( " xml " , ' version="1.0" ' ) ;
builder . element ( " package " , nest: ( ) {
builder
. namespace ( " http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd " ) ;
builder . element ( " metadata " , nest: ( ) {
builder . element ( " id " , nest: " sass " ) ;
builder . element ( " title " , nest: " Sass " ) ;
builder . element ( " version " , nest: _chocolateyVersion ( ) ) ;
builder . element ( " authors " , nest: " Natalie Weizenbaum " ) ;
builder . element ( " owners " , nest: " nex3 " ) ;
builder . element ( " projectUrl " , nest: " https://github.com/sass/dart-sass " ) ;
builder . element ( " licenseUrl " ,
nest: " https://github.com/sass/dart-sass/blob/ $ _version /LICENSE " ) ;
builder . element ( " iconUrl " ,
nest: " https://cdn.rawgit.com/sass/sass-site/ "
" f99ee33e4f688e244c7a5902c59d61f78daccc55/source/assets/img/ "
" logos/logo-seal.png " ) ;
builder . element ( " bugTrackerUrl " ,
nest: " https://github.com/sass/dart-sass/issues " ) ;
builder . element ( " description " ,
nest: """
* * Sass makes CSS fun again * * . Sass is an extension of CSS , adding nested rules , variables , mixins , selector inheritance , and more . It ' s translated to well-formatted, standard CSS using the command line tool or a web-framework plugin.
This package is Dart Sass , the new Dart implementation of Sass .
""" );
builder . element ( " summary " , nest: " Sass makes CSS fun again. " ) ;
builder . element ( " tags " , nest: " css preprocessor style sass " ) ;
builder . element ( " copyright " ,
nest: " Copyright ${ new DateTime . now ( ) . year } Google, Inc. " ) ;
builder . element ( " dependencies " , nest: ( ) {
builder . element ( " dependency " , attributes: {
" id " : " dart-sdk " ,
// Unfortunately we need the exact same Dart version as we built with,
// since we ship a snapshot which isn't cross-version compatible. Once
// we switch to native compilation this won't be an issue.
" version " : " [ $ _dartVersion ] " ,
} ) ;
} ) ;
} ) ;
} ) ;
return builder . build ( ) as xml . XmlDocument ;
}
/// The current Sass version, formatted for Chocolatey which doesn't allow dots
/// in prerelease versions.
String _chocolateyVersion ( ) {
var components = _version . split ( " - " ) ;
if ( components . length = = 1 ) return components . first ;
assert ( components . length = = 2 ) ;
return " ${ components . first } - ${ components . last . replaceAll ( ' . ' , ' ' ) } " ;
}
/// Returns the contents of the `properties.psmdcp` file, computed from the
/// nuspec's XML.
String _nupkgProperties ( xml . XmlDocument nuspec ) {
var builder = new xml . XmlBuilder ( ) ;
builder . processing ( " xml " , ' version="1.0" ' ) ;
builder . element ( " coreProperties " , nest: ( ) {
builder . namespace (
" http://schemas.openxmlformats.org/package/2006/metadata/core-properties " ) ;
builder . namespace ( " http://purl.org/dc/elements/1.1/ " , " dc " ) ;
builder . element ( " dc:creator " ,
nest: nuspec . findAllElements ( " authors " ) . first . text ) ;
builder . element ( " dc:description " ,
nest: nuspec . findAllElements ( " description " ) . first . text ) ;
builder . element ( " dc:identifier " ,
nest: nuspec . findAllElements ( " id " ) . first . text ) ;
builder . element ( " version " ,
nest: nuspec . findAllElements ( " version " ) . first . text ) ;
builder . element ( " keywords " ,
nest: nuspec . findAllElements ( " tags " ) . first . text ) ;
builder . element ( " dc:title " ,
nest: nuspec . findAllElements ( " title " ) . first . text ) ;
} ) ;
return builder . build ( ) . toString ( ) ;
}
2016-10-05 10:04:51 +02:00
/// Ensure that the `build/` directory exists.
void _ensureBuild ( ) {
new Directory ( ' build ' ) . createSync ( recursive: true ) ;
}
2016-10-08 00:46:58 +02:00
2016-10-15 00:48:34 +02:00
/// Builds a standalone Sass package for the given [os] and [architecture].
///
/// The [client] is used to download the corresponding Dart SDK.
Future _buildPackage ( http . Client client , String os , String architecture ) async {
// TODO: Compile a single executable that embeds the Dart VM and the snapshot.
var url = " https://storage.googleapis.com/dart-archive/channels/stable/ "
" release/ $ _dartVersion /sdk/dartsdk- $ os - $ architecture -release.zip " ;
log ( " Downloading $ url ... " ) ;
var response = await client . get ( Uri . parse ( url ) ) ;
if ( response . statusCode ~ / 100 ! = 2 ) {
throw " Failed to download package: ${ response . statusCode } "
" ${ response . reasonPhrase } . " ;
}
var dartExecutable = new ZipDecoder ( )
. decodeBytes ( response . bodyBytes )
. firstWhere ( ( file ) = > os = = ' windows '
? file . name . endsWith ( " /bin/dart.exe " )
: file . name . endsWith ( " /bin/dart " ) ) ;
var executable = dartExecutable . content ;
var archive = new Archive ( )
2017-01-14 01:13:26 +01:00
. . addFile ( _fileFromBytes (
2016-11-02 21:01:47 +01:00
" dart-sass/src/dart ${ os = = ' windows ' ? ' .exe ' : ' ' } " , executable ,
executable: true ) )
2017-01-14 01:13:26 +01:00
. . addFile ( _file ( " dart-sass/src/DART_LICENSE " , p . join ( _sdkDir , ' LICENSE ' ) ) )
. . addFile (
_file ( " dart-sass/src/sass.dart.snapshot " , " build/sass.dart.snapshot " ) )
. . addFile ( _file ( " dart-sass/src/SASS_LICENSE " , " LICENSE " ) )
. . addFile ( _fileFromString (
" dart-sass/dart-sass ${ os = = ' windows ' ? ' .bat ' : ' ' } " ,
_readAndReplaceVersion (
" package/dart-sass. ${ os = = ' windows ' ? ' bat ' : ' sh ' } " ) ,
executable: true ) ) ;
2016-10-15 00:48:34 +02:00
var prefix = ' build/dart-sass- $ _version - $ os - $ architecture ' ;
if ( os = = ' windows ' ) {
var output = " $ prefix .zip " ;
log ( " Creating $ output ... " ) ;
new File ( output ) . writeAsBytesSync ( new ZipEncoder ( ) . encode ( archive ) ) ;
} else {
var output = " $ prefix .tar.gz " ;
log ( " Creating $ output ... " ) ;
new File ( output ) . writeAsBytesSync (
new GZipEncoder ( ) . encode ( new TarEncoder ( ) . encode ( archive ) ) ) ;
}
}
2017-01-14 01:13:26 +01:00
/// Reads [file], replaces all instances of SASS_VERSION with the actual
/// version, and returns its contents.
String _readAndReplaceVersion ( String file ) = >
new File ( file ) . readAsStringSync ( ) . replaceAll ( " SASS_VERSION " , _version ) ;
2016-10-15 00:48:34 +02:00
/// Creates an [ArchiveFile] with the given [path] and [data].
///
/// If [executable] is `true`, this marks the file as executable.
2017-01-14 01:13:26 +01:00
ArchiveFile _fileFromBytes ( String path , List < int > data ,
{ bool executable: false } ) = >
2016-10-15 00:48:34 +02:00
new ArchiveFile ( path , data . length , data )
. . mode = executable ? 495 : 428
. . lastModTime = new DateTime . now ( ) . millisecondsSinceEpoch ~ / 1000 ;
2017-01-14 01:13:26 +01:00
/// Creates a UTF-8-encoded [ArchiveFile] with the given [path] and [contents].
///
/// If [executable] is `true`, this marks the file as executable.
ArchiveFile _fileFromString ( String path , String contents ,
{ bool executable: false } ) = >
_fileFromBytes ( path , UTF8 . encode ( contents ) , executable: executable ) ;
/// Creates an [ArchiveFile] at the archive path [target] from the local file at
/// [source].
///
/// If [executable] is `true`, this marks the file as executable.
ArchiveFile _file ( String target , String source , { bool executable: false } ) = >
_fileFromBytes ( target , new File ( source ) . readAsBytesSync ( ) ,
executable: executable ) ;