Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ All Dart (https://dartlang.org) projects eventually share a common set of develo
- Consistent code formatting
- Static analysis to detect issues
- Examples for manual testing/exploration
- Applying a LICENSE file to all source files

Together, the Dart SDK and a couple of packages from the Dart team supply the necessary tooling to support the above
requirements. But, the usage is inconsistent, configuration is limited to command-line arguments, and you inevitably end
Expand Down Expand Up @@ -59,6 +60,7 @@ static analysis - you just need to know how to use the `dart_dev` tool.
- **Code Formatting:** runs the [`dartfmt` tool from the `dart_style` package](https://github.com/dart-lang/dart_style) over source code.
- **Static Analysis:** runs the [`dartanalyzer`](https://www.dartlang.org/tools/analyzer/) over source code.
- **Serving Examples:** uses [`pub serve`](https://www.dartlang.org/tools/pub/cmd/pub-serve.html) to serve the project examples.
- **Applying a License to Source Files:** copies a LICENSE file to all applicable files.


## Getting Started
Expand Down Expand Up @@ -87,6 +89,9 @@ main(args) async {
// Define the entry points for static analysis.
config.analyze.entryPoints = ['lib/', 'test/', 'tool/'];

// Define the directories where the LICENSE should be applied.
config.copyLicense.directories = ['example/', 'lib/'];

// Configure the port on which examples should be served.
config.examples.port = 9000;

Expand Down Expand Up @@ -114,12 +119,14 @@ see the help usage. Try it out by running any of the following tasks:
```
# with the alias
ddev analyze
ddev copy-license
ddev examples
ddev format
ddev test

# without the alias
pub run dart_dev analyze
pub run dart_dev copy-license
pub run dart_dev examples
pub run dart_dev format
pub run dart_dev test
Expand All @@ -138,6 +145,7 @@ import 'package:dart_dev/dart_dev.dart';
main(args) async {
// Available config objects:
// config.analyze
// config.copyLicense
// config.examples
// config.format
// config.init
Expand All @@ -156,6 +164,15 @@ Name | Type | Default | Description
`fatalWarnings` | `bool` | `true` | Treat non-type warnings as fatal.
`hints` | `bool` | `true` | Show hint results.

### `copy-license` Config
All configuration options for the `copy-license` task are found on the `config.copyLicense` object.

Name | Type | Default | Description
------------- | -------------- | ---------- | -----------
`directories` | `List<String>` | `['lib/']` | All source files in these directories will have the LICENSE header applied.
`licensePath` | `String` | `LICENSE` | Path to the source LICENSE file that will be copied to all source files.


### `examples` Config
All configuration options for the `examples` task are found on the `config.examples` object.

Expand Down Expand Up @@ -202,13 +219,15 @@ Usage: pub run dart_dev [task] [options]
Supported tasks:

analyze
copy-license
examples
format
init
test
```

- Static analysis: `ddev analyze`
- Applying license to source files: `ddev copy-license`
- Serving examples: `ddev examples`
- Dart formatter: `ddev format`
- Initialization: `ddev init`
Expand Down
2 changes: 2 additions & 0 deletions lib/src/dart_dev_cli.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:dart_dev/src/tasks/cli.dart';
import 'package:dart_dev/src/tasks/config.dart';

import 'package:dart_dev/src/tasks/analyze/cli.dart';
import 'package:dart_dev/src/tasks/copy_license/cli.dart';
import 'package:dart_dev/src/tasks/examples/cli.dart';
import 'package:dart_dev/src/tasks/format/cli.dart';
import 'package:dart_dev/src/tasks/init/cli.dart';
Expand All @@ -38,6 +39,7 @@ String _topLevelUsage = _parser.usage;

dev(List<String> args) async {
registerTask(new AnalyzeCli(), config.analyze);
registerTask(new CopyLicenseCli(), config.copyLicense);
registerTask(new ExamplesCli(), config.examples);
registerTask(new FormatCli(), config.format);
registerTask(new InitCli(), config.init);
Expand Down
2 changes: 2 additions & 0 deletions lib/src/tasks/config.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
library dart_dev.src.tasks.config;

import 'package:dart_dev/src/tasks/analyze/config.dart';
import 'package:dart_dev/src/tasks/copy_license/config.dart';
import 'package:dart_dev/src/tasks/examples/config.dart';
import 'package:dart_dev/src/tasks/format/config.dart';
import 'package:dart_dev/src/tasks/init/config.dart';
Expand All @@ -10,6 +11,7 @@ Config config = new Config();

class Config {
AnalyzeConfig analyze = new AnalyzeConfig();
CopyLicenseConfig copyLicense = new CopyLicenseConfig();
ExamplesConfig examples = new ExamplesConfig();
FormatConfig format = new FormatConfig();
InitConfig init = new InitConfig();
Expand Down
92 changes: 92 additions & 0 deletions lib/src/tasks/copy_license/api.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
library dart_dev.src.tasks.copy_license.api;

import 'dart:async';
import 'dart:io';

import 'package:dart_dev/src/tasks/copy_license/config.dart';
import 'package:dart_dev/src/tasks/task.dart';

CopyLicenseTask copyLicense(
{List<String> directories: defaultDirectories,
String licensePath: defaultLicensePath}) {
CopyLicenseTask task = new CopyLicenseTask();

File license = new File(licensePath);
if (!license.existsSync()) throw new Exception(
'License file "$licensePath" does not exist.');

String licenseContents = license.readAsStringSync();
directories.forEach((path) {
Directory dir = new Directory(path);
if (!dir.existsSync()) return;
Iterable<File> files =
dir.listSync(recursive: true).where((e) => e is File);
files.forEach((file) {
// Skip files in packages/ directory
if (file.path.contains('/packages/')) return;

if (applyLicense(file, licenseContents)) {
task.affectedFiles.add(file.path);
}
});
});

task.successful = true;
return task;
}

bool applyLicense(File file, String license) {
if (hasLicense(file, license)) return false;

String fileLicense;
try {
fileLicense = licenseForFileType(file, license);
} catch (e) {
return false;
}

String fileContents = file.readAsStringSync();
file.writeAsStringSync('$fileLicense\n$fileContents');
return true;
}

bool hasLicense(File file, String license) {
String licenseHeader = license.split('\n').first;
String fileContents = file.readAsStringSync();
Iterable<String> lines = fileContents.split('\n');
if (lines.isEmpty) return false;
if (lines.first.contains(licenseHeader)) return true;
if (lines.length <= 1) return false;
if (lines.elementAt(1).contains(licenseHeader)) return true;
return false;
}

String licenseForFileType(File file, String license) {
String opening = '';
String closing = '';
String linePrefix = '';

if (file.path.endsWith('.css')) {
opening = '/**\n';
linePrefix = ' * ';
closing = '\n */';
} else if (file.path.endsWith('.dart')) {
linePrefix = '// ';
} else if (file.path.endsWith('.html')) {
opening = '<!--\n';
closing = '\n-->';
} else if (file.path.endsWith('.js')) {
linePrefix = '// ';
} else {
throw new ArgumentError('Unsupported file type: ${file.path}');
}

String l = license.split('\n').map((l) => '$linePrefix$l').join('\n');
return '$opening$l$closing\n';
}

class CopyLicenseTask extends Task {
List<String> affectedFiles = [];
final Future done = new Future.value();
CopyLicenseTask();
}
38 changes: 38 additions & 0 deletions lib/src/tasks/copy_license/cli.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
library dart_dev.src.tasks.copy_license.cli;

import 'dart:async';
import 'dart:io';

import 'package:args/args.dart';

import 'package:dart_dev/src/tasks/copy_license/api.dart';
import 'package:dart_dev/src/tasks/cli.dart';
import 'package:dart_dev/src/tasks/config.dart';

class CopyLicenseCli extends TaskCli {
final ArgParser argParser = new ArgParser();

final String command = 'copy-license';

Future<CliResult> run(ArgResults parsedArgs) async {
List<String> directories = config.copyLicense.directories;
String licensePath = config.copyLicense.licensePath;

if (!(new File(licensePath)).existsSync()) return new CliResult.fail(
'License file "$licensePath" does not exist.');

CopyLicenseTask task =
copyLicense(directories: directories, licensePath: licensePath);
await task.done;
if (task.successful) {
int numFiles = task.affectedFiles.length;
String fileSummary = '\n ${task.affectedFiles.join('\n ')}';
String result = numFiles == 0
? 'License already exists on all files.'
: 'License successfully applied to $numFiles files:$fileSummary';
return new CliResult.success(result);
} else {
return new CliResult.fail('License application failed.');
}
}
}
11 changes: 11 additions & 0 deletions lib/src/tasks/copy_license/config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
library dart_dev.src.tasks.copy_license.config;

import 'package:dart_dev/src/tasks/config.dart';

const List<String> defaultDirectories = const ['lib/'];
const String defaultLicensePath = 'LICENSE';

class CopyLicenseConfig extends TaskConfig {
List<String> directories = defaultDirectories;
String licensePath = defaultLicensePath;
}
1 change: 1 addition & 0 deletions lib/src/tasks/init/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ main(List<String> args) async {

// Available task configurations:
// config.analyze
// config.copyLicense
// config.examples
// config.format
// config.test
Expand Down
13 changes: 13 additions & 0 deletions test/fixtures/copy_license/has_licenses/NONSTANDARDLICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
25 changes: 25 additions & 0 deletions test/fixtures/copy_license/has_licenses/example/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!--
Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
</head>
<body>
This should be copyrighted.
</body>
</html>
19 changes: 19 additions & 0 deletions test/fixtures/copy_license/has_licenses/example/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright [yyyy] [name of copyright owner]
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

library copy_license_no_licenses;

class TopSecret {
String info = '[redacted]';
}
17 changes: 17 additions & 0 deletions test/fixtures/copy_license/has_licenses/example/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright [yyyy] [name of copyright owner]
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

(function () {
document.body = null;
})();
20 changes: 20 additions & 0 deletions test/fixtures/copy_license/has_licenses/example/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright [yyyy] [name of copyright owner]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

* {
background-color: #000;
color: #000;
}
5 changes: 5 additions & 0 deletions test/fixtures/copy_license/has_licenses/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: copy_license_has_licenses
version: 0.0.0
dev_dependencies:
dart_dev:
path: ../../../..
11 changes: 11 additions & 0 deletions test/fixtures/copy_license/has_licenses/tool/dev.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
library tool.dev;

import 'package:dart_dev/dart_dev.dart' show dev, config;

main(List<String> args) async {
config.copyLicense
..directories = ['example/']
..licensePath = 'NONSTANDARDLICENSE';

await dev(args);
}
5 changes: 5 additions & 0 deletions test/fixtures/copy_license/no_license_file/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: copy_license_no_license_file
version: 0.0.0
dev_dependencies:
dart_dev:
path: ../../../..
Loading