-
Notifications
You must be signed in to change notification settings - Fork 214
File based asset system #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
**/*.txt.copy |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
import 'dart:async'; | ||
import 'dart:convert'; | ||
import 'dart:io'; | ||
|
||
import 'package:path/path.dart' as path; | ||
|
||
import '../asset/asset.dart'; | ||
import '../asset/exceptions.dart'; | ||
import '../asset/id.dart'; | ||
import '../asset/reader.dart'; | ||
import '../asset/writer.dart'; | ||
import '../package_graph/package_graph.dart'; | ||
import 'exceptions.dart'; | ||
|
||
/// Basic [AssetReader] which uses a [PackageGraph] to look up where to read | ||
/// files from disk. | ||
class FileBasedAssetReader implements AssetReader { | ||
final PackageGraph packageGraph; | ||
|
||
FileBasedAssetReader(this.packageGraph); | ||
|
||
@override | ||
Future<bool> hasInput(AssetId id) async { | ||
_checkInput(id); | ||
return _fileFor(id, packageGraph).exists(); | ||
} | ||
|
||
@override | ||
Future<String> readAsString(AssetId id, {Encoding encoding: UTF8}) async { | ||
_checkInput(id); | ||
|
||
var file = await _fileFor(id, packageGraph); | ||
if (!await file.exists()) { | ||
throw new AssetNotFoundException(id); | ||
} | ||
return file.readAsString(encoding: encoding); | ||
} | ||
|
||
/// Checks that [id] is a valid input, and throws an [InvalidInputException] | ||
/// if its not. | ||
void _checkInput(AssetId id) { | ||
if (id.package != packageGraph.root.name && !id.path.startsWith('lib/')) { | ||
throw new InvalidInputException(id); | ||
} | ||
} | ||
} | ||
|
||
/// Basic [AssetWriter] which uses a [PackageGraph] to look up where to write | ||
/// files to disk. | ||
class FileBasedAssetWriter implements AssetWriter { | ||
final PackageGraph packageGraph; | ||
|
||
FileBasedAssetWriter(this.packageGraph); | ||
|
||
@override | ||
Future writeAsString(Asset asset, {Encoding encoding: UTF8}) async { | ||
if (asset.id.package != packageGraph.root.name) { | ||
throw new InvalidOutputException(asset); | ||
} | ||
|
||
var file = _fileFor(asset.id, packageGraph); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this method There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The writer doesn't have enough knowledge to know whether that is ok (it is fine to overwrite a generated file from a previous build, just not a previous phase). This has to be implemented at the BuildStep/Transformer level |
||
await file.create(recursive: true); | ||
await file.writeAsString(asset.stringContents, encoding: encoding); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the answer is no, but should we be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know a ton about when that would be necessary, but I don't think it is? This will eventually be caching these files as well, at which point it really won't matter. |
||
} | ||
} | ||
|
||
/// Returns a [File] for [id] given [packageGraph]. | ||
File _fileFor(AssetId id, PackageGraph packageGraph) { | ||
var package = packageGraph[id.package]; | ||
if (package == null) { | ||
throw new PackageNotFoundException(id.package); | ||
} | ||
return new File(path.join(package.location.toFilePath(), id.path)); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
@TestOn('vm') | ||
import 'dart:io'; | ||
|
||
import 'package:test/test.dart'; | ||
|
||
import 'package:build/build.dart'; | ||
|
||
import '../common/common.dart'; | ||
|
||
final packageGraph = new PackageGraph.forPath('test/fixtures/basic_pkg'); | ||
|
||
main() async { | ||
|
||
group('FileBasedAssetReader', () { | ||
final reader = new FileBasedAssetReader(packageGraph); | ||
|
||
test('can read any application package files', () async { | ||
expect(await reader.readAsString(makeAssetId('basic_pkg|hello.txt')), | ||
'world\n'); | ||
expect(await reader.readAsString(makeAssetId('basic_pkg|lib/hello.txt')), | ||
'world\n'); | ||
expect(await reader.readAsString(makeAssetId('basic_pkg|web/hello.txt')), | ||
'world\n'); | ||
}); | ||
|
||
test('can only read package dependency files in the lib dir', () async { | ||
expect(await reader.readAsString(makeAssetId('a|lib/a.txt')), 'A\n'); | ||
expect(reader.readAsString(makeAssetId('a|web/a.txt')), | ||
throwsA(invalidInputException)); | ||
expect(reader.readAsString(makeAssetId('a|a.txt')), | ||
throwsA(invalidInputException)); | ||
}); | ||
|
||
test('can check for existence of any application package files', () async { | ||
expect(await reader.hasInput(makeAssetId('basic_pkg|hello.txt')), isTrue); | ||
expect(await reader.hasInput(makeAssetId('basic_pkg|lib/hello.txt')), | ||
isTrue); | ||
expect(await reader.hasInput(makeAssetId('basic_pkg|web/hello.txt')), | ||
isTrue); | ||
|
||
expect(await reader.hasInput(makeAssetId('basic_pkg|a.txt')), isFalse); | ||
expect( | ||
await reader.hasInput(makeAssetId('basic_pkg|lib/a.txt')), isFalse); | ||
}); | ||
|
||
test('can only check for existence of package dependency files in lib', | ||
() async { | ||
expect(await reader.hasInput(makeAssetId('a|lib/a.txt')), isTrue); | ||
expect(await reader.hasInput(makeAssetId('a|lib/b.txt')), isFalse); | ||
expect(reader.hasInput(makeAssetId('a|web/a.txt')), | ||
throwsA(invalidInputException)); | ||
expect(reader.hasInput(makeAssetId('a|a.txt')), | ||
throwsA(invalidInputException)); | ||
expect(reader.hasInput(makeAssetId('foo|bar.txt')), | ||
throwsA(invalidInputException)); | ||
}); | ||
|
||
test('throws when attempting to read a non-existent file', () async { | ||
expect(reader.readAsString(makeAssetId('basic_pkg|foo.txt')), | ||
throwsA(assetNotFoundException)); | ||
expect(reader.readAsString(makeAssetId('a|lib/b.txt')), | ||
throwsA(assetNotFoundException)); | ||
expect(reader.readAsString(makeAssetId('foo|lib/bar.txt')), | ||
throwsA(packageNotFoundException)); | ||
}); | ||
}); | ||
|
||
group('FileBasedAssetWriter', () { | ||
final writer = new FileBasedAssetWriter(packageGraph); | ||
|
||
test('can output files in the application package', () async { | ||
var asset = makeAsset('basic_pkg|test_file.txt', 'test'); | ||
await writer.writeAsString(asset); | ||
var id = asset.id; | ||
var file = new File('test/fixtures/${id.package}/${id.path}'); | ||
expect(await file.exists(), isTrue); | ||
expect(await file.readAsString(), 'test'); | ||
await file.delete(); | ||
}); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. n/a |
||
test('can\'t output files in package dependencies', () async { | ||
var asset = makeAsset('a|test.txt'); | ||
expect(writer.writeAsString(asset), throwsA(invalidOutputException)); | ||
}); | ||
|
||
test('can\'t output files in arbitrary packages', () async { | ||
var asset = makeAsset('foo|bar.txt'); | ||
expect(writer.writeAsString(asset), throwsA(invalidOutputException)); | ||
}); | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
import 'package:build/build.dart'; | ||
|
||
import 'in_memory_writer.dart'; | ||
|
||
int _nextId = 0; | ||
AssetId makeAssetId([String assetIdString]) { | ||
if (assetIdString == null) { | ||
assetIdString = 'a|web/asset_$_nextId.txt'; | ||
_nextId++; | ||
} | ||
return new AssetId.parse(assetIdString); | ||
} | ||
|
||
Asset makeAsset([String assetIdString, String contents]) { | ||
var id = makeAssetId(assetIdString); | ||
return new Asset(id, contents ?? '$id'); | ||
} | ||
|
||
Map<AssetId, Asset> makeAssets(Map<String, String> assetsMap) { | ||
var assets = <AssetId, Asset>{}; | ||
assetsMap.forEach((idString, content) { | ||
var asset = makeAsset(idString, content); | ||
assets[asset.id] = asset; | ||
}); | ||
return assets; | ||
} | ||
|
||
void addAssets(Iterable<Asset> assets, InMemoryAssetWriter writer) { | ||
for (var asset in assets) { | ||
writer.assets[asset.id] = asset.stringContents; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider reusing
Error#safeToString
in exceptiontoString
methods.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tried that out but the message doesn't seem very good? (all you get is the type)