diff --git a/lib/src/command/bootstrap.dart b/lib/src/command/bootstrap.dart index 6631fe254..a27868c9d 100644 --- a/lib/src/command/bootstrap.dart +++ b/lib/src/command/bootstrap.dart @@ -1,6 +1,24 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:io'; import 'package:args/command_runner.dart' show Command; +import 'package:melos/src/common/intellij_project.dart'; import '../command_runner.dart'; import '../common/logger.dart'; @@ -23,6 +41,7 @@ class BootstrapCommand extends Command { '${logger.ansi.yellow}\$${logger.ansi.noColor} ${logger.ansi.emphasized("melos bootstrap")}'); logger.stdout( ' └> ${logger.ansi.cyan}${logger.ansi.emphasized(currentWorkspace.path)}${logger.ansi.noColor}\n'); + var successMessage = '${logger.ansi.green}SUCCESS${logger.ansi.noColor}'; var bootstrapProgress = logger.progress('Bootstrapping project'); await currentWorkspace.generatePubspecFile(); @@ -34,16 +53,22 @@ class BootstrapCommand extends Command { exit(1); } - bootstrapProgress.finish( - message: '${logger.ansi.green}SUCCESS${logger.ansi.noColor}', - showTiming: true); + bootstrapProgress.finish(message: successMessage, showTiming: true); + if (Platform.isWindows) { + // TODO Manual print finish status as it doesn't show on Windows, bug with progress library. + print(' > $successMessage'); + } + var linkingProgress = logger.progress('Linking project packages'); await currentWorkspace.linkPackages(); + currentWorkspace.clean(cleanPackages: false); - linkingProgress.finish( - message: '${logger.ansi.green}SUCCESS${logger.ansi.noColor}', - showTiming: true); + linkingProgress.finish(message: successMessage, showTiming: true); + if (Platform.isWindows) { + // TODO Manual print finish status as it doesn't show on Windows, bug with progress library. + print(' > $successMessage'); + } if (currentWorkspace.config.scripts.containsKey('postbootstrap')) { logger.stdout('Running postbootstrap script...\n'); @@ -59,5 +84,7 @@ class BootstrapCommand extends Command { }); logger.stdout( '\n -> ${currentWorkspace.packages.length} plugins bootstrapped'); + + await IntellijProject.fromWorkspace(currentWorkspace).writeFiles(); } } diff --git a/lib/src/command/clean.dart b/lib/src/command/clean.dart index 02a511638..a37731a3a 100644 --- a/lib/src/command/clean.dart +++ b/lib/src/command/clean.dart @@ -1,4 +1,22 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'package:args/command_runner.dart' show Command; +import 'package:melos/src/common/intellij_project.dart'; import '../command_runner.dart'; import '../common/logger.dart'; @@ -13,17 +31,18 @@ class CleanCommand extends Command { @override final String description = - 'Clean this workspace and all packages. This deletes the temporary pub files such as ".packages" & ".flutter-plugins". Supports all package filtering options.'; + 'Clean this workspace and all packages. This deletes the temporary pub & ide files such as ".packages" & ".flutter-plugins". Supports all package filtering options.'; @override void run() async { logger.stdout('Cleaning workspace...'); currentWorkspace.clean(); + await IntellijProject.fromWorkspace(currentWorkspace).cleanFiles(); if (currentWorkspace.config.scripts.containsKey('postclean')) { logger.stdout('Running postclean script...\n'); await MelosCommandRunner.instance.run(['run', 'postclean']); } logger.stdout( - 'Workspace cleaned, you will need to run the bootstrap command again.'); + '\nWorkspace cleaned. You will need to run the bootstrap command again to use this workspace.'); } } diff --git a/lib/src/command/exec.dart b/lib/src/command/exec.dart index b86725b53..45232859a 100644 --- a/lib/src/command/exec.dart +++ b/lib/src/command/exec.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:io'; import 'package:args/command_runner.dart' show Command; @@ -25,7 +42,7 @@ class ExecCommand extends Command { defaultsTo: false, negatable: true, help: - 'Wether exec should fail fast and not execute the script in further packages if the script fails in a individual package.'); + 'Whether exec should fail fast and not execute the script in further packages if the script fails in a individual package.'); } @override diff --git a/lib/src/command/run.dart b/lib/src/command/run.dart index 90b0a7c4c..9dcc35ef5 100644 --- a/lib/src/command/run.dart +++ b/lib/src/command/run.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:io'; import 'package:args/command_runner.dart' show Command; diff --git a/lib/src/command/unpublished.dart b/lib/src/command/unpublished.dart index 0e7129009..f6f743f8d 100644 --- a/lib/src/command/unpublished.dart +++ b/lib/src/command/unpublished.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:io'; import 'package:args/command_runner.dart' show Command; diff --git a/lib/src/command_runner.dart b/lib/src/command_runner.dart index 45e2ad6e0..9a3f1f4c7 100644 --- a/lib/src/command_runner.dart +++ b/lib/src/command_runner.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:io'; import 'package:args/args.dart'; @@ -68,6 +85,7 @@ class MelosCommandRunner extends CommandRunner { // TODO(salakar): log init help once init command complete logger.stderr( 'Your current directory does not appear to be a valid workspace.'); + logger.stderr('Does the "melos.yaml" file exist in the root?'); exit(1); } diff --git a/lib/src/common/intellij_project.dart b/lib/src/common/intellij_project.dart new file mode 100644 index 000000000..3bc5d9215 --- /dev/null +++ b/lib/src/common/intellij_project.dart @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + +import 'dart:io'; + +import 'package:meta/meta.dart'; +import 'package:path/path.dart' show joinAll; + +import '../common/package.dart'; +import '../common/utils.dart' as utils; +import '../common/workspace.dart'; + +const String _kTemplatesDirName = 'templates'; +const String _kIntellijDirName = 'intellij'; +const String _kDotIdeaDirName = '.idea'; +const String _kTmplExtension = '.tmpl'; + +/// IntelliJ project IDE configuration helper. +class IntellijProject { + final MelosWorkspace _workspace; + + final Map _cacheTemplates = {}; + + IntellijProject._(this._workspace); + + /// Build a new [IntellijProject] from a [MelosWorkspace]. + static IntellijProject fromWorkspace(MelosWorkspace workspace) { + return IntellijProject._(workspace); + } + + /// Fully qualified path to the intellij templates shipped as part of Melos. + String get pathTemplates { + return joinAll([ + utils.getMelosRoot(), + _kTemplatesDirName, + _kIntellijDirName, + ]); + } + + /// Path to the .idea folder in the current workspace. + String get pathDotIdea { + return joinAll([_workspace.path, _kDotIdeaDirName]); + } + + /// Path to the .idea/.name file in the current workspace. + /// This file generated with the workspace name as its contents. IntelliJ + /// uses this to change the project display name the IDE. + String get pathDotName { + return joinAll([pathDotIdea, '.name']); + } + + /// Path to the .idea/modules.xml file in the current workspace. + /// This file is generated with a module for each discovered package in the + /// current workspace. + String get pathModulesXml { + return joinAll([pathDotIdea, 'modules.xml']); + } + + String pathTemplatesForDirectory(String directory) { + return joinAll([pathTemplates, directory]); + } + + String pathPackageModuleIml(MelosPackage package) { + return joinAll([package.path, '${package.name}.iml']); + } + + String injectTemplateVariable( + {@required String template, + @required String variableName, + @required String variableValue}) { + return template.replaceAll('{{#$variableName}}', variableValue); + } + + String injectTemplateVariables( + String template, Map variables) { + String updatedTemplate = template; + variables.forEach((key, value) { + updatedTemplate = injectTemplateVariable( + template: updatedTemplate, variableName: key, variableValue: value); + }); + return updatedTemplate; + } + + String ideaModuleStringForName(String moduleName, {String relativePath}) { + String module = ''; + if (relativePath == null) { + module = + ''; + } else { + module = + ''; + } + // Pad to preserve formatting on generated file. Indent x6. + return ' $module'; + } + + /// Reads a file template from the templates directory. + /// Additionally keeps a cache to reduce reads. + Future readFileTemplate(String fileName, + {String templateCategory}) async { + if (_cacheTemplates[fileName] != null) { + return _cacheTemplates[fileName]; + } + String templatesRootPath; + if (templateCategory != null) { + templatesRootPath = pathTemplatesForDirectory(templateCategory); + } else { + templatesRootPath = pathTemplates; + } + + File templateFile = + File(joinAll([templatesRootPath, '$fileName$_kTmplExtension'])); + + String template = await templateFile.readAsString(); + _cacheTemplates[fileName] = template; + return template; + } + + Future forceWriteToFile(String filePath, String fileContents) async { + File outputFile = File(filePath); + await outputFile.create(recursive: true); + await outputFile.writeAsString(fileContents); + } + + /// Create a .name file using the workspace name. + /// This gets picked up by the IDE and is used for display purposes. + Future writeNameFile() { + return forceWriteToFile(pathDotName, _workspace.config.name); + } + + String moduleTemplateFileForPackageType(PackageType type) { + switch (type) { + case PackageType.flutterPackage: + case PackageType.flutterPlugin: + return 'flutter_plugin_module.iml'; + case PackageType.flutterApp: + return 'flutter_app_module.iml'; + case PackageType.dartPackage: + default: + return 'dart_package_module.iml'; + } + } + + Future writePackageModule(MelosPackage package) async { + String template = await readFileTemplate( + moduleTemplateFileForPackageType(package.type), + templateCategory: 'modules'); + return forceWriteToFile(pathPackageModuleIml(package), template); + } + + Future writeWorkspaceModule() async { + String ideaWorkspaceModuleImlTemplate = await readFileTemplate( + 'workspace_root_module.iml', + templateCategory: 'modules'); + String workspaceModuleName = _workspace.config.name.toLowerCase(); + return forceWriteToFile( + joinAll([_workspace.path, '$workspaceModuleName.iml']), + ideaWorkspaceModuleImlTemplate); + } + + Future writeModulesXml() async { + List ideaModules = []; + String workspaceModuleName = _workspace.config.name.toLowerCase(); + _workspace.packages.forEach((package) { + ideaModules.add(ideaModuleStringForName(package.name, + relativePath: package.pathRelativeToWorkspace)); + }); + ideaModules.add(ideaModuleStringForName(workspaceModuleName)); + String ideaModulesXmlTemplate = await readFileTemplate('modules.xml'); + String generatedModulesXml = injectTemplateVariable( + template: ideaModulesXmlTemplate, + variableName: 'modules', + variableValue: ideaModules.join('\n')); + return forceWriteToFile(pathModulesXml, generatedModulesXml); + } + + String getMelosBinForIde() { + if (Platform.isWindows) { + return r'$USER_HOME$/AppData/Local/Pub/Cache/bin/melos.bat'; + } + return r'$USER_HOME$/.pub-cache/bin/melos'; + } + + Future writeMelosScripts() async { + String melosScriptTemplate = await readFileTemplate('shell_script.xml', + templateCategory: 'runConfigurations'); + + Map runConfigurations = { + 'Melos -> Bootstrap Workspace': 'bootstrap', + 'Melos -> Clean Workspace': 'clean', + }; + _workspace.config.scripts.keys.forEach((key) { + runConfigurations["Melos Run -> '$key'"] = 'run $key'; + }); + + await Future.forEach(runConfigurations.keys, (String scriptName) async { + String scriptArgs = runConfigurations[scriptName]; + String generatedRunConfiguration = + injectTemplateVariables(melosScriptTemplate, { + 'scriptName': scriptName, + 'scriptArgs': scriptArgs, + 'scriptPath': getMelosBinForIde(), + }); + String outputFile = joinAll([ + pathDotIdea, + 'runConfigurations', + 'melos_${scriptArgs.replaceAll(' ', '_').replaceAll(':', '_')}.xml' + ]); + await forceWriteToFile(outputFile, generatedRunConfiguration); + }); + } + + Future cleanFiles() async { + // TODO + } + + Future writeFlutterRunScripts() async { + // TODO + } + + Future writeFlutterTestScripts() async { + // TODO + } + + Future writeFiles() async { + // /.idea/.name + await writeNameFile(); + + // //.iml + await Future.forEach(_workspace.packages, (MelosPackage package) async { + await writePackageModule(package); + }); + + // /.iml + await writeWorkspaceModule(); + + // /.idea/modules.xml + await writeModulesXml(); + + // /.idea/runConfigurations/.xml + await writeMelosScripts(); + + // TODO - don't do anything for now + await writeFlutterRunScripts(); + await writeFlutterTestScripts(); + } +} diff --git a/lib/src/common/logger.dart b/lib/src/common/logger.dart index 4a18c0e1d..9eb79fca0 100644 --- a/lib/src/common/logger.dart +++ b/lib/src/common/logger.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'package:cli_util/cli_logging.dart'; Logger logger = Logger.standard(); diff --git a/lib/src/common/package.dart b/lib/src/common/package.dart index 7f63b7221..6157ee96e 100644 --- a/lib/src/common/package.dart +++ b/lib/src/common/package.dart @@ -1,8 +1,26 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:yaml/yaml.dart'; +import 'package:path/path.dart' show relative, normalize; import 'package:http/http.dart' as http; import '../pub/pub_file.dart'; @@ -33,72 +51,102 @@ const String kAndroid = 'android'; /// Key for Web platform. const String kWeb = 'web'; +// Various pubspec yaml keys. +const String _kName = 'name'; +const String _kVersion = 'version'; +const String _kPublishTo = 'publish_to'; +const String _kDependencies = 'dependencies'; +const String _kDevDependencies = 'dev_dependencies'; +const String _kFlutter = 'flutter'; +const String _kPlugin = 'plugin'; + +/// Enum representing what type of package this is. +enum PackageType { + dartPackage, + flutterPackage, + flutterPlugin, + flutterApp, +} + +/// A workspace representation of a Dart package. class MelosPackage { + final MelosWorkspace _workspace; final Map _yamlContents; List _registryVersions; - final String _name; + final String _path; + /// Package name. + /// As defined in pubspec.yaml. String get name => _name; - String get version => _yamlContents['version'] as String; - - final String _path; + /// Package version. + /// As defined in pubspec.yaml. + String get version => _yamlContents[_kVersion] as String; + /// Package path. + /// Fully qualified path to this package location. String get path => _path; - MelosPackage._(this._name, this._path, this._yamlContents); + /// Package path as a normalized sting relative to the root of the workspace. + /// e.g. "packages/firebase_database". + String get pathRelativeToWorkspace => + normalize(relative(_path, from: _workspace.path)); + + /// Type of this package, e.g. [PackageType.flutterApp]. + PackageType get type { + if (isFlutterApp) return PackageType.flutterApp; + if (isFlutterPlugin) return PackageType.flutterPlugin; + if (isFlutterPackage) return PackageType.flutterPackage; + return PackageType.dartPackage; + } - Set get dependenciesSet { - if (_yamlContents['dependencies'] != null) { + MelosPackage._(this._name, this._path, this._yamlContents, this._workspace); + + /// Dependencies of this package. + /// Sourced from pubspec.yaml. + Map get dependencies { + if (_yamlContents[_kDependencies] != null) { // ignore: omit_local_variable_types - Set keysSet = {}; - _yamlContents['dependencies'].keys.forEach((key) { - keysSet.add(key as String); + Map deps = {}; + _yamlContents[_kDependencies].keys.forEach((key) { + deps[key as String] = _yamlContents[_kDependencies][key]; }); - return keysSet; + return deps; } return {}; } + /// Dev dependencies of this package. + /// Sourced from pubspec.yaml. Map get devDependencies { - if (_yamlContents['dev_dependencies'] != null) { + if (_yamlContents[_kDevDependencies] != null) { // ignore: omit_local_variable_types Map devDeps = {}; - _yamlContents['dev_dependencies'].keys.forEach((key) { - devDeps[key as String] = _yamlContents['dev_dependencies'][key]; + _yamlContents[_kDevDependencies].keys.forEach((key) { + devDeps[key as String] = _yamlContents[_kDevDependencies][key]; }); return devDeps; } return {}; } - Set get devDependenciesSet { - if (_yamlContents['dev_dependencies'] != null) { - // ignore: omit_local_variable_types - Set keysSet = {}; - _yamlContents['dev_dependencies'].keys.forEach((key) { - keysSet.add(key as String); - }); - return keysSet; - } - return {}; - } - - static Future fromPubspecPath( - FileSystemEntity pubspecPath) async { + /// Build a Melos representation of a package from a pubspec file. + static Future fromPubspecPathAndWorkspace( + FileSystemEntity pubspecPath, MelosWorkspace workspace) async { final yamlFileContents = await loadYamlFile(pubspecPath.path); if (yamlFileContents == null) return null; - final pluginName = yamlFileContents['name'] as String; + final pluginName = yamlFileContents[_kName] as String; return MelosPackage._( - pluginName, pubspecPath.parent.path, yamlFileContents); + pluginName, pubspecPath.parent.path, yamlFileContents, workspace); } + /// Builds a dependency graph of this packages dependencies and their dependents. Future> getDependencyGraph({bool includeDev = true}) async { var dependencyGraph = {}; - var workspaceGraph = await currentWorkspace.getDependencyGraph(); + var workspaceGraph = await _workspace.getDependencyGraph(); - dependenciesSet.forEach((name) { + dependencies.keys.toSet().forEach((name) { dependencyGraph.add(name); var children = workspaceGraph[name]; if (children != null && children.isNotEmpty) { @@ -107,7 +155,7 @@ class MelosPackage { }); if (includeDev) { - devDependenciesSet.forEach((name) { + devDependencies.keys.toSet().forEach((name) { dependencyGraph.add(name); var children = workspaceGraph[name]; if (children != null && children.isNotEmpty) { @@ -119,22 +167,25 @@ class MelosPackage { return dependencyGraph; } - /// Execute a command from this packages root directory. + /// Execute a shell command inside this package. Future exec(List execArgs) async { final packagePrefix = '[${logger.ansi.blue + logger.ansi.emphasized(_name) + logger.ansi.noColor}]: '; var environment = { 'MELOS_PACKAGE_NAME': name, - 'MELOS_PACKAGE_VERSION': version ?? '0.0.0', + 'MELOS_PACKAGE_VERSION': version ?? 'none', 'MELOS_PACKAGE_PATH': path, - 'MELOS_ROOT_PATH': currentWorkspace.path, + 'MELOS_ROOT_PATH': _workspace.path, }; + // TODO what if it's not called 'example'? if (path.endsWith('example')) { var exampleParentPackagePath = Directory(path).parent.path; - var exampleParentPackage = await fromPubspecPath(File( - '$exampleParentPackagePath${Platform.pathSeparator}pubspec.yaml')); + var exampleParentPackage = await fromPubspecPathAndWorkspace( + File( + '$exampleParentPackagePath${Platform.pathSeparator}pubspec.yaml'), + _workspace); if (exampleParentPackage != null) { environment['MELOS_PARENT_PACKAGE_NAME'] = exampleParentPackage.name; environment['MELOS_PARENT_PACKAGE_VERSION'] = @@ -149,21 +200,32 @@ class MelosPackage { prefix: packagePrefix); } - // TODO(salakar): Conditionally write these files only if they exist in root. - // TODO(salakar): Only write Flutter specific files to packages that are Flutter plugins. + /// Generates Pub/Flutter related temporary files such as .packages or pubspec.lock. Future linkPackages(MelosWorkspace workspace) async { + // Dart specific files. await Future.forEach([ PackagesPubFile.fromWorkspacePackage(workspace, this), - FlutterPluginsPubFile.fromWorkspacePackage(workspace, this), PubspecLockPubFile.fromWorkspacePackage(workspace, this), PackageConfigPubFile.fromWorkspacePackage(workspace, this), - FlutterDependenciesPubFile.fromWorkspacePackage(workspace, this), ], (Future future) async { PubFile pubFile = await future; return pubFile.write(); }); + + // Additional Flutter application specific files, only if package is an App. + if (isFlutterApp) { + await Future.forEach([ + FlutterPluginsPubFile.fromWorkspacePackage(workspace, this), + FlutterDependenciesPubFile.fromWorkspacePackage(workspace, this), + ], (Future future) async { + PubFile pubFile = await future; + return pubFile.write(); + }); + } } + /// Queries the pub.dev registry for published versions of this package. + /// Primarily used for publish filters and versioning. Future> getPublishedVersions() async { if (_registryVersions != null) { return _registryVersions; @@ -186,36 +248,152 @@ class MelosPackage { return _registryVersions; } + /// Cleans up all Melos generated files for this package. void clean() { PackagesPubFile.fromDirectory(path).delete(); - FlutterPluginsPubFile.fromDirectory(path).delete(); PubspecLockPubFile.fromDirectory(path).delete(); PackageConfigPubFile.fromDirectory(path).delete(); - FlutterDependenciesPubFile.fromDirectory(path).delete(); + if (isFlutterPackage) { + FlutterPluginsPubFile.fromDirectory(path).delete(); + FlutterDependenciesPubFile.fromDirectory(path).delete(); + } } - bool isFlutterPackage() { - final YamlMap dependencies = _yamlContents['dependencies'] as YamlMap; + /// Returns whether this package is for Flutter. + /// This is determined by whether the package depends on the Flutter SDK. + bool get isFlutterPackage { + final YamlMap dependencies = _yamlContents[_kDependencies] as YamlMap; if (dependencies == null) { return false; } - return dependencies.containsKey('flutter'); + return dependencies.containsKey(_kFlutter); } - bool supportsFlutterPlatform(String platform) { + /// Returns whether this package is a Flutter app. + /// This is determined by ensuring all the following conditions are met: + /// a) the package depends on the Flutter SDK. + /// b) the package does not define itself as a Flutter plugin inside pubspec.yaml. + bool get isFlutterApp { + // Must directly depend on the Flutter SDK. + if (!isFlutterPackage) return false; + // Must not have a Flutter plugin definition in it's pubspec.yaml. + final YamlMap flutterSection = _yamlContents[_kFlutter] as YamlMap; + if (flutterSection == null) { + return true; + } + final YamlMap pluginSection = flutterSection[_kPlugin] as YamlMap; + if (pluginSection == null) { + return true; + } + // Package is a plugin not an app. + return false; + } + + /// Returns whether this package supports Flutter for Android. + bool get flutterAppSupportsAndroid { + if (!isFlutterApp) return false; + return _flutterAppSupportsPlatform(kAndroid); + } + + /// Returns whether this package supports Flutter for Web. + bool get flutterAppSupportsWeb { + if (!isFlutterApp) return false; + return _flutterAppSupportsPlatform(kWeb); + } + + /// Returns whether this package supports Flutter for Windows. + bool get flutterAppSupportsWindows { + if (!isFlutterApp) return false; + return _flutterAppSupportsPlatform(kWindows); + } + + /// Returns whether this package supports Flutter for MacOS. + bool get flutterAppSupportsMacos { + if (!isFlutterApp) return false; + return _flutterAppSupportsPlatform(kMacos); + } + + /// Returns whether this package supports Flutter for Linux. + bool get flutterAppSupportsLinux { + if (!isFlutterApp) return false; + return _flutterAppSupportsPlatform(kLinux); + } + + /// Returns whether this package is a Flutter plugin. + /// This is determined by whether the pubspec contains a flutter.plugin definition. + bool get isFlutterPlugin { + final YamlMap flutterSection = _yamlContents[_kFlutter] as YamlMap; + if (flutterSection == null) { + return false; + } + final YamlMap pluginSection = flutterSection[_kPlugin] as YamlMap; + if (pluginSection == null) { + return false; + } + return true; + } + + /// Returns whether this package supports Flutter for Android. + bool get flutterPluginSupportsAndroid { + if (!isFlutterPlugin) return false; + return _flutterPluginSupportsPlatform(kAndroid); + } + + /// Returns whether this package supports Flutter for Web. + bool get flutterPluginSupportsWeb { + if (!isFlutterPlugin) return false; + return _flutterPluginSupportsPlatform(kWeb); + } + + /// Returns whether this package supports Flutter for Windows. + bool get flutterPluginSupportsWindows { + if (!isFlutterPlugin) return false; + return _flutterPluginSupportsPlatform(kWindows); + } + + /// Returns whether this package supports Flutter for MacOS. + bool get flutterPluginSupportsMacos { + if (!isFlutterPlugin) return false; + return _flutterPluginSupportsPlatform(kMacos); + } + + /// Returns whether this package supports Flutter for Linux. + bool get flutterPluginSupportsLinux { + if (!isFlutterPlugin) return false; + return _flutterPluginSupportsPlatform(kLinux); + } + + /// Returns whether this package is private (publish_to set to 'none'). + bool get isPrivate { + if (!_yamlContents.containsKey(_kPublishTo)) return false; + if (_yamlContents[_kPublishTo].runtimeType != String) return false; + return _yamlContents[_kPublishTo] == 'none'; + } + + bool _flutterAppSupportsPlatform(String platform) { assert(platform == kIos || platform == kAndroid || platform == kWeb || platform == kMacos || platform == kWindows || platform == kLinux); + return File('$path${Platform.pathSeparator}$platform').existsSync(); + } - final YamlMap flutterSection = _yamlContents['flutter'] as YamlMap; + bool _flutterPluginSupportsPlatform(String platform) { + assert(platform == kIos || + platform == kAndroid || + platform == kWeb || + platform == kMacos || + platform == kWindows || + platform == kLinux); + + final YamlMap flutterSection = _yamlContents[_kFlutter] as YamlMap; if (flutterSection == null) { return false; } - final YamlMap pluginSection = flutterSection['plugin'] as YamlMap; + final YamlMap pluginSection = flutterSection[_kPlugin] as YamlMap; if (pluginSection == null) { return false; } @@ -228,32 +406,6 @@ class MelosPackage { return platforms.containsKey(platform); } - /// Returns whether the given directory contains a Flutter web plugin. - bool supportsFlutterWeb() { - return supportsFlutterPlatform(kWeb); - } - - /// Returns whether the given directory contains a Flutter Windows plugin. - bool supportsFlutterWindows() { - return supportsFlutterPlatform(kWindows); - } - - /// Returns whether the given directory contains a Flutter macOS plugin. - bool isMacOsPlugin() { - return supportsFlutterPlatform(kMacos); - } - - /// Returns whether the given directory contains a Flutter linux plugin. - bool isLinuxPlugin() { - return supportsFlutterPlatform(kLinux); - } - - bool isPrivate() { - if (!_yamlContents.containsKey('publish_to')) return false; - if (_yamlContents['publish_to'].runtimeType != String) return false; - return _yamlContents['publish_to'] == 'none'; - } - @override String toString() { return 'MelosPackage[$name@$version]'; diff --git a/lib/src/common/utils.dart b/lib/src/common/utils.dart index cc4a0fc3c..dc61fd61e 100644 --- a/lib/src/common/utils.dart +++ b/lib/src/common/utils.dart @@ -1,12 +1,35 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:path/path.dart' show relative; +import 'package:path/path.dart' show relative, normalize, windows; import 'package:yaml/yaml.dart'; import 'logger.dart'; +var _didLogRmWarning = false; + +String getMelosRoot() { + return File.fromUri(Platform.script).parent.parent.path; +} + String getAndroidSdkRoot() { var possibleSdkRoot = Platform.environment['ANDROID_SDK_ROOT']; if (possibleSdkRoot == null) { @@ -17,6 +40,7 @@ String getAndroidSdkRoot() { return possibleSdkRoot; } +// TODO not Windows compatible String getFlutterSdkRoot() { var result = Process.runSync('which', ['flutter']); var possiblePath = result.stdout.toString(); @@ -52,7 +76,10 @@ String pubspecPathForDirectory(Directory pluginDirectory) { } String relativePath(String path, String from) { - return relative(path, from: from); + if (Platform.isWindows) { + return windows.normalize(path).replaceAll(r'\', r'\\'); + } + return normalize(relative(path, from: from)); } /// Simple check to see if the [Directory] qualifies as a plugin repository. @@ -73,30 +100,57 @@ Future startProcess(List execArgs, bool onlyOutputOnError = false}) async { final environmentVariables = environment ?? {}; final workingDirectoryPath = workingDirectory ?? Directory.current.path; + final executable = Platform.isWindows ? 'cmd' : '/bin/sh'; + final filteredArgs = execArgs.map((arg) { + var _arg = arg; - final executable = - Platform.isWindows ? '%WINDIR%\\System32\\cmd.exe' : '/bin/sh'; + // Remove empty args. + if (_arg.trim().isEmpty) { + return null; + } - final execProcess = await Process.start(executable, [], - workingDirectory: workingDirectoryPath, - includeParentEnvironment: true, - environment: environmentVariables); + // Attempt to make line continuations Windows & Linux compatible. + if (_arg.trim() == r'\') { + return Platform.isWindows ? _arg.replaceAll(r'\', '^') : _arg; + } + if (_arg.trim() == r'^') { + return Platform.isWindows ? _arg : _arg.replaceAll('^', r'\'); + } - final execString = execArgs.map((arg) { - var _arg = arg; + // Inject Melos variables if any. environment.forEach((key, value) { _arg = _arg.replaceAll('\$$key', value); _arg = _arg.replaceAll(key, value); }); - return _arg; - }).join(' '); - execProcess.stdin.writeln(execString); + return _arg; + }).where((element) => element != null); + + // TODO This is just a temporary workaround to keep FlutterFire working on Windows + // TODO until all the run scripts have been updated in its melos.yaml file. + if (filteredArgs.toList()[0] == 'rm' && Platform.isWindows) { + if (!_didLogRmWarning) { + print( + '> Warning: skipped executing a script as "rm" is not supported on Windows.'); + _didLogRmWarning = true; + } + return 0; + } - // exit with the exit code of the previous command - if (Platform.isWindows) { - execProcess.stdin.writeln('exit /b %errorlevel%'); - } else { + final execProcess = await Process.start( + executable, Platform.isWindows ? ['/C', '%MELOS_SCRIPT%'] : [], + workingDirectory: workingDirectoryPath, + includeParentEnvironment: true, + environment: { + ...environmentVariables, + 'MELOS_SCRIPT': filteredArgs.join(' '), + }, + runInShell: Platform.isWindows); + + if (!Platform.isWindows) { + // Pipe in the arguments to trigger the script to run. + execProcess.stdin.writeln(filteredArgs.join(' ')); + // Exit the process with the same exit code as the previous command. execProcess.stdin.writeln('exit \$?'); } @@ -126,22 +180,31 @@ Future startProcess(List execArgs, .transform>(utf8.encoder); } - var stdoutSubscriber; - var stderrSubscriber; - - if (!onlyOutputOnError) { - stdoutSubscriber = stdoutStream.listen(stdout.add); - stderrSubscriber = stderrStream.listen(stderr.add); - } - + final List processStdout = []; + final List processStderr = []; + final Completer processStdoutCompleter = Completer(); + final Completer processStderrCompleter = Completer(); + + stdoutStream.listen((List event) { + processStdout.addAll(event); + if (!onlyOutputOnError) { + stdout.add(event); + } + }, onDone: () => processStdoutCompleter.complete()); + stderrStream.listen((List event) { + processStderr.addAll(event); + if (!onlyOutputOnError) { + stderr.add(event); + } + }, onDone: () => processStderrCompleter.complete()); + + await processStdoutCompleter.future; + await processStderrCompleter.future; var exitCode = await execProcess.exitCode; - if (!onlyOutputOnError) { - await stdoutSubscriber.cancel(); - await stderrSubscriber.cancel(); - } else if (exitCode > 0) { - (await execProcess.stdout.toList()).forEach(stdout.add); - (await execProcess.stderr.toList()).forEach(stdout.add); + if (onlyOutputOnError && exitCode > 0) { + stdout.add(processStdout); + stderr.add(processStderr); } return exitCode; diff --git a/lib/src/common/workspace.dart b/lib/src/common/workspace.dart index d8afd1a5e..29416c185 100644 --- a/lib/src/common/workspace.dart +++ b/lib/src/common/workspace.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:async'; import 'dart:io'; @@ -29,7 +46,7 @@ class MelosWorkspace { String get path => _path; - Map> _dependencyGraph; + Map> _cacheDependencyGraph; final MelosWorkspaceConfig _config; @@ -61,7 +78,6 @@ class MelosWorkspace { bool skipPrivate, bool published}) async { if (_packages != null) return Future.value(_packages); - final packageGlobs = _config.packages; var filterResult = Directory(_path) @@ -78,7 +94,7 @@ class MelosWorkspace { return matchedPattern != null; }).asyncMap((entity) { // Convert into Package for further filtering - return MelosPackage.fromPubspecPath(entity); + return MelosPackage.fromPubspecPathAndWorkspace(entity, this); }); if (scope.isNotEmpty) { @@ -137,7 +153,7 @@ class MelosWorkspace { if (skipPrivate) { // Whether we should skip packages with 'publish_to: none' set. filterResult = filterResult.where((package) { - return !package.isPrivate(); + return !package.isPrivate; }); } @@ -169,9 +185,10 @@ class MelosWorkspace { return _packages; } + /// Builds a dependency graph of dependencies and their dependents in this workspace. Future>> getDependencyGraph() async { - if (_dependencyGraph != null) { - return _dependencyGraph; + if (_cacheDependencyGraph != null) { + return _cacheDependencyGraph; } final pubListCommandOutput = await Process.run( @@ -218,7 +235,7 @@ class MelosWorkspace { dependencyGraphFlat[entry.name] = entriesSet; }); - _dependencyGraph = dependencyGraphFlat; + _cacheDependencyGraph = dependencyGraphFlat; return dependencyGraphFlat; } @@ -242,7 +259,7 @@ class MelosWorkspace { } void clean({bool cleanPackages = true}) { - // clean workspace + // Clean workspace. PackagesPubFile.fromDirectory(path).delete(); FlutterPluginsPubFile.fromDirectory(path).delete(); PackageConfigPubFile.fromDirectory(path).delete(); @@ -292,7 +309,7 @@ class MelosWorkspace { // TODO(salakar): this is a hacky work around for dev deps - look at using // `pub cache add` etc and manually generating file:// links var devDependencies = plugin.devDependencies; - plugin.devDependenciesSet.forEach((name) { + plugin.devDependencies.keys.toSet().forEach((name) { var linkedPackageExists = packages.firstWhere((package) { return package.name == name; }, orElse: () { diff --git a/lib/src/common/workspace_config.dart b/lib/src/common/workspace_config.dart index a53825856..a1b4c261c 100644 --- a/lib/src/common/workspace_config.dart +++ b/lib/src/common/workspace_config.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:io'; import 'package:glob/glob.dart'; @@ -5,6 +22,8 @@ import 'package:yaml/yaml.dart'; import 'utils.dart'; +// TODO validation of config +// name should be required, alphanumeric dasherized/underscored class MelosWorkspaceConfig { final String _name; diff --git a/lib/src/pub/pub_deps_list.dart b/lib/src/pub/pub_deps_list.dart index 8b5547b55..28d331af2 100644 --- a/lib/src/pub/pub_deps_list.dart +++ b/lib/src/pub/pub_deps_list.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'package:collection/collection.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:string_scanner/string_scanner.dart'; diff --git a/lib/src/pub/pub_file.dart b/lib/src/pub/pub_file.dart index fd38429cd..de014ca8e 100644 --- a/lib/src/pub/pub_file.dart +++ b/lib/src/pub/pub_file.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:io'; class PubFile { diff --git a/lib/src/pub/pub_file_flutter_dependencies.dart b/lib/src/pub/pub_file_flutter_dependencies.dart index bfb682d46..8b8e90e51 100644 --- a/lib/src/pub/pub_file_flutter_dependencies.dart +++ b/lib/src/pub/pub_file_flutter_dependencies.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:collection'; import 'dart:convert'; import 'dart:io'; diff --git a/lib/src/pub/pub_file_flutter_plugins.dart b/lib/src/pub/pub_file_flutter_plugins.dart index 4316e586a..cb629b578 100644 --- a/lib/src/pub/pub_file_flutter_plugins.dart +++ b/lib/src/pub/pub_file_flutter_plugins.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:io'; import '../common/package.dart'; diff --git a/lib/src/pub/pub_file_package_config.dart b/lib/src/pub/pub_file_package_config.dart index 3931f9d71..4ffcc1c20 100644 --- a/lib/src/pub/pub_file_package_config.dart +++ b/lib/src/pub/pub_file_package_config.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:collection'; import 'dart:convert'; import 'dart:io'; diff --git a/lib/src/pub/pub_file_packages.dart b/lib/src/pub/pub_file_packages.dart index e5d0604c7..511b762e4 100644 --- a/lib/src/pub/pub_file_packages.dart +++ b/lib/src/pub/pub_file_packages.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:io'; import '../common/package.dart'; diff --git a/lib/src/pub/pub_file_pubspec_lock.dart b/lib/src/pub/pub_file_pubspec_lock.dart index 9fb006782..c692dcac5 100644 --- a/lib/src/pub/pub_file_pubspec_lock.dart +++ b/lib/src/pub/pub_file_pubspec_lock.dart @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + import 'dart:collection'; import 'dart:convert'; import 'dart:io'; diff --git a/pubspec.yaml b/pubspec.yaml index d96a898a3..eaf307dbb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,7 +10,7 @@ dependencies: pool: ^1.4.0 collection: ^1.14.12 string_scanner: ^1.0.5 - cli_util: ^0.1.4 + cli_util: ^0.2.0 glob: ^1.2.0 path: ^1.7.0 yaml: ^2.2.1 diff --git a/templates/intellij/modules.xml.tmpl b/templates/intellij/modules.xml.tmpl new file mode 100644 index 000000000..bed32a044 --- /dev/null +++ b/templates/intellij/modules.xml.tmpl @@ -0,0 +1,8 @@ + + + + +{{#modules}} + + + \ No newline at end of file diff --git a/templates/intellij/modules/dart_package_module.iml.tmpl b/templates/intellij/modules/dart_package_module.iml.tmpl new file mode 100644 index 000000000..389d07a14 --- /dev/null +++ b/templates/intellij/modules/dart_package_module.iml.tmpl @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/intellij/modules/flutter_app_android_java_module.iml.tmpl b/templates/intellij/modules/flutter_app_android_java_module.iml.tmpl new file mode 100644 index 000000000..20173fe72 --- /dev/null +++ b/templates/intellij/modules/flutter_app_android_java_module.iml.tmpl @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/templates/intellij/modules/flutter_app_module.iml.tmpl b/templates/intellij/modules/flutter_app_module.iml.tmpl new file mode 100644 index 000000000..26a45e792 --- /dev/null +++ b/templates/intellij/modules/flutter_app_module.iml.tmpl @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/intellij/modules/flutter_plugin_android_java_module.iml.tmpl b/templates/intellij/modules/flutter_plugin_android_java_module.iml.tmpl new file mode 100644 index 000000000..29787d04c --- /dev/null +++ b/templates/intellij/modules/flutter_plugin_android_java_module.iml.tmpl @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + diff --git a/templates/intellij/modules/flutter_plugin_module.iml.tmpl b/templates/intellij/modules/flutter_plugin_module.iml.tmpl new file mode 100644 index 000000000..9fc8ce79a --- /dev/null +++ b/templates/intellij/modules/flutter_plugin_module.iml.tmpl @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/intellij/modules/workspace_root_module.iml.tmpl b/templates/intellij/modules/workspace_root_module.iml.tmpl new file mode 100644 index 000000000..8be8bf887 --- /dev/null +++ b/templates/intellij/modules/workspace_root_module.iml.tmpl @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/templates/intellij/runConfigurations/flutter_run.xml.tmpl b/templates/intellij/runConfigurations/flutter_run.xml.tmpl new file mode 100644 index 000000000..70f2f6f01 --- /dev/null +++ b/templates/intellij/runConfigurations/flutter_run.xml.tmpl @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/templates/intellij/runConfigurations/flutter_test.xml.tmpl b/templates/intellij/runConfigurations/flutter_test.xml.tmpl new file mode 100644 index 000000000..aa53b9168 --- /dev/null +++ b/templates/intellij/runConfigurations/flutter_test.xml.tmpl @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/templates/intellij/runConfigurations/shell_script.xml.tmpl b/templates/intellij/runConfigurations/shell_script.xml.tmpl new file mode 100644 index 000000000..6c18bccf5 --- /dev/null +++ b/templates/intellij/runConfigurations/shell_script.xml.tmpl @@ -0,0 +1,11 @@ + + + + + diff --git a/templates/intellij/vcs.xml.tmpl b/templates/intellij/vcs.xml.tmpl new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/templates/intellij/vcs.xml.tmpl @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/templates/melos/melos.yaml.tmpl b/templates/melos/melos.yaml.tmpl new file mode 100644 index 000000000..f87f5c14c --- /dev/null +++ b/templates/melos/melos.yaml.tmpl @@ -0,0 +1 @@ +# TODO \ No newline at end of file