// Copyright 2016 The Chromium Authors. 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:io'; import 'package:path/path.dart' as path; import 'package:mustache4dart/mustache4dart.dart' as mustache; import 'artifacts.dart'; import 'globals.dart'; const String _kTemplateExtension = '.tmpl'; const String _kCopyTemplateExtension = '.copy.tmpl'; /// Expands templates in a directory to a destination. All files that must /// undergo template expansion should end with the '.tmpl' extension. All other /// files are ignored. In case the contents of entire directories must be copied /// as is, the directory itself can end with '.tmpl' extension. Files within /// such a directory may also contain the '.tmpl' extension and will be /// considered for expansion. In case certain files need to be copied but /// without template expansion (images, data files, etc.), the '.copy.tmpl' /// extension may be used. /// /// Files in the destination will not contain either the '.tmpl' or '.copy.tmpl' /// extensions. class Template { Template(Directory templateSource, Directory baseDir) { _templateFilePaths = new Map<String, String>(); if (!templateSource.existsSync()) { return; } List<FileSystemEntity> templateFiles = templateSource.listSync(recursive: true); for (FileSystemEntity entity in templateFiles) { if (entity is! File) { // We are only interesting in template *file* URIs. continue; } String relativePath = path.relative(entity.path, from: baseDir.absolute.path); if (relativePath.contains(_kTemplateExtension)) { // If '.tmpl' appears anywhere within the path of this entity, it is // is a candidate for rendering. This catches cases where the folder // itself is a template. _templateFilePaths[relativePath] = path.absolute(entity.path); } } } factory Template.fromName(String name) { // All named templates are placed in the 'templates' directory Directory templateDir = _templateDirectoryInPackage(name); return new Template(templateDir, templateDir); } Map<String /* relative */, String /* absolute source */> _templateFilePaths; void render(Directory destination, Map<String, dynamic> context, { bool overwriteExisting: true }) { destination.createSync(recursive: true); String destinationDirPath = destination.absolute.path; _templateFilePaths.forEach((String relativeDestPath, String absoluteSrcPath) { String finalDestinationPath = path .join(destinationDirPath, relativeDestPath) .replaceAll(_kCopyTemplateExtension, '') .replaceAll(_kTemplateExtension, ''); File finalDestinationFile = new File(finalDestinationPath); String relativePathForLogging = path.relative(finalDestinationFile.path); // Step 1: Check if the file needs to be overwritten. if (finalDestinationFile.existsSync()) { if (overwriteExisting) { finalDestinationFile.delete(recursive: true); printStatus(' $relativePathForLogging (overwritten)'); } else { // The file exists but we cannot overwrite it, move on. printStatus(' $relativePathForLogging (existing - skipped)'); return; } } else { printStatus(' $relativePathForLogging'); } finalDestinationFile.createSync(recursive: true); File sourceFile = new File(absoluteSrcPath); // Step 2: If the absolute paths ends with a 'copy.tmpl', this file does // not need mustache rendering but needs to be directly copied. if (sourceFile.path.endsWith(_kCopyTemplateExtension)) { finalDestinationFile.writeAsBytesSync(sourceFile.readAsBytesSync()); return; } // Step 3: If the absolute path ends with a '.tmpl', this file needs // rendering via mustache. if (sourceFile.path.endsWith(_kTemplateExtension)) { String templateContents = sourceFile.readAsStringSync(); String renderedContents = mustache.render(templateContents, context); finalDestinationFile.writeAsStringSync(renderedContents); return; } // Step 4: This file does not end in .tmpl but is in a directory that // does. Directly copy the file to the destination. finalDestinationFile.writeAsBytesSync(sourceFile.readAsBytesSync()); }); } } Directory _templateDirectoryInPackage(String name) { String templatesDir = path.join(ArtifactStore.flutterRoot, 'packages', 'flutter_tools', 'templates'); return new Directory(path.join(templatesDir, name)); }