Skip to content
Draft
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
23 changes: 17 additions & 6 deletions packages/static_shock_cli/lib/src/commands/create_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:static_shock_cli/src/project_maintenance/build_project.dart';
import 'package:static_shock_cli/src/templates/blog_template.dart';
import 'package:static_shock_cli/src/templates/docs_multi_page_template.dart';
import 'package:static_shock_cli/src/templates/empty_template.dart';
import 'package:static_shock_cli/src/templates/tailwind_template.dart';
import 'package:static_shock_cli/src/version_check.dart';

/// CLI [Command] that generates a new Static Shock project based on user preferences.
Expand All @@ -16,6 +17,7 @@ class CreateCommand extends Command with PubVersionCheck {
// TODO: create single-page docs template
// _TemplateType.docsSinglePage: "Documentation (single page)",
_TemplateType.empty: "Empty",
_TemplateType.tailwind: "Tailwind CSS",
};

CreateCommand(this.log);
Expand All @@ -27,7 +29,8 @@ class CreateCommand extends Command with PubVersionCheck {
final name = "create";

@override
final description = "Creates a new static site project at the desired location.";
final description =
"Creates a new static site project at the desired location.";

@override
bool get takesArguments => true;
Expand All @@ -36,9 +39,11 @@ class CreateCommand extends Command with PubVersionCheck {
Future<void> run() async {
await super.run();

log.info("Welcome to Static Shock! Let's get you started with a new project!");
log.info(
"Welcome to Static Shock! Let's get you started with a new project!");
log.info("");
log.info("We'll ask you a series of questions, and then we'll generate a custom website project just for you.");
log.info(
"We'll ask you a series of questions, and then we'll generate a custom website project just for you.");
log.info("");

final templateType = log.chooseOne(
Expand All @@ -50,7 +55,8 @@ class CreateCommand extends Command with PubVersionCheck {

log.info("You chose: ${_templateNamesById[templateType]}.");
log.info("");
log.info("We'll ask you a series of questions to configure the template for your specific needs...");
log.info(
"We'll ask you a series of questions to configure the template for your specific needs...");
log.info("");

late final Directory targetDirectory;
Expand All @@ -63,10 +69,13 @@ class CreateCommand extends Command with PubVersionCheck {
targetDirectory = await runMultiPageDocsTemplateWizard(log);
case _TemplateType.empty:
targetDirectory = await runEmptyTemplateWizard(log);
case _TemplateType.tailwind:
targetDirectory = await runTailwindTemplateWizard(log);
}

log.success("Your new Static Shock website has been generated!\n");
final shouldInitialize = log.confirm("Would you like to immediately run a build of your new website?");
final shouldInitialize = log.confirm(
"Would you like to immediately run a build of your new website?");
log.info("");

log.info("--------------------------------------");
Expand All @@ -78,7 +87,8 @@ class CreateCommand extends Command with PubVersionCheck {
log.info("");

log.success("Congratulations, your new Static Shock website is ready!");
log.detail("To learn how to further configure your website, please check our guides at https://staticshock.io");
log.detail(
"To learn how to further configure your website, please check our guides at https://staticshock.io");
}
}

Expand All @@ -88,4 +98,5 @@ enum _TemplateType {
// docsSinglePage,
docsMultiPage,
empty,
tailwind,
}
152 changes: 152 additions & 0 deletions packages/static_shock_cli/lib/src/templates/tailwind_template.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import 'dart:io';
import 'dart:isolate';

import 'package:args/command_runner.dart';
import 'package:mason/mason.dart';
import 'package:static_shock_cli/src/project_maintenance/build_project.dart';
import 'package:static_shock_cli/src/templates/basic_template_cli.dart';

/// Walks the user through a selection of details that are required to generate
/// and then generates the project.
Future<Directory> runTailwindTemplateWizard(Logger log) async {
final configuration = BasicTemplateConfigurator.promptForConfiguration(log);
final targetDirectory =
BasicTemplateConfigurator.promptForOutputDirectory(log);

await generateTailwindTemplate(
targetDirectory,
projectName: configuration.projectName,
projectDescription: configuration.projectDescription,
);

return targetDirectory;
}

/// A CLI command that can be directly run with provided arguments, to generate
/// a new Static Shock project with a minimal collection of files.
class TailwindTemplateCommand extends Command {
static const argProjectName = "project-name";
static const argProjectDescription = "project-description";
static const argAutoInitialize = "auto-initialize";

TailwindTemplateCommand(this.log) {
argParser
..addOption(
argProjectName,
help:
"The name of the new Static Shock project - it should be a valid Dart package name.",
mandatory: true,
)
..addOption(
argProjectDescription,
help:
"The description of the new Static Shock project - will be added to the project's pubspec file.",
defaultsTo: "",
)
..addFlag(
argAutoInitialize,
help:
"True if this command should immediately run 'dart pub get' and 'shock build' after generating the project.",
defaultsTo: true,
);
}

final Logger log;

@override
String get name => "tailwind";

@override
String get description =>
"Generate a new, Static Shock project with TailwindCSS pre-configured.";

@override
Future<void> run() async {
if (!argResults!.wasParsed(argProjectName)) {
log.err("Argument $argProjectName is required.");
printUsage();
return;
}
final projectName = argResults![argProjectName] as String;
final projectDescription = argResults![argProjectDescription] as String;

final targetPath = argResults!.rest.lastOrNull;
final targetDirectory =
targetPath != null ? Directory(targetPath) : Directory.current;
if (!targetDirectory.existsSync()) {
try {
targetDirectory.createSync(recursive: true);
} catch (exception) {
log.err(
"Failed to create project directory: ${targetDirectory.absolute}");
log.err("$exception");
return;
}
}

await generateTailwindTemplate(
targetDirectory,
projectName: projectName,
projectDescription: projectDescription,
);

log.success("Successfully created a new Static Shock project!\n");

if (argResults![argAutoInitialize]) {
// Run "shock build".
await Project.build(log: log, workingDirectory: targetDirectory);

// Run "pub get".
await Project.pubGet(log: log, workingDirectory: targetDirectory);
}

log.success("Congratulations, your Static Shock project is ready to go!");
log.info("\nTo learn how to use Static Shock, check out staticshock.io\n");
}
}

/// Generates a new Static Shock project using the "tailwind" template, placing
/// the files in the given [targetDirectory].
///
/// The generated files are configured with the given properties.
Future<void> generateTailwindTemplate(
Directory targetDirectory, {
required String projectName,
required String projectDescription,
}) async {
final bundle = await _loadTemplateBundle();
final generator = await MasonGenerator.fromBundle(bundle);
final target = DirectoryGeneratorTarget(targetDirectory);

await generator.generate(target, vars: {
_templateKeyProjectName: projectName,
_templateKeyProjectDescription: projectDescription,
});
}

Future<MasonBundle> _loadTemplateBundle() async {
// We expect to run as a globally activated Dart package. To access assets bundled
// with our package, we need to resolve a package path to a file system path, as
// shown below.
//
// Note: Dart automatically looks under "lib/" within a package. When reading the
// path below, mentally insert "/lib/" between the package name and the first sub-directory.
//
// Reference: https://stackoverflow.com/questions/72255508/how-to-get-the-file-path-to-an-asset-included-in-a-dart-package
// TODO: How to create a "tailwind.bundle"
final packageUri =
Uri.parse('package:static_shock_cli/templates/tailwind.bundle');
Comment on lines +136 to +138
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still need to work out how to create this .bundle. Please advise.

final absoluteUri = await Isolate.resolvePackageUri(packageUri);

final file = File.fromUri(absoluteUri!);
if (!file.existsSync()) {
throw Exception(
"Couldn't locate the Static Shock 'tailwind' template in the package assets. Looked in: '${file.path}'");
}

// Decode the file's bytes into a Mason bundle. Return it.
return await MasonBundle.fromUniversalBundle(file.readAsBytesSync());
}

const _templateKeyProjectName = "project_name";
const _templateKeyProjectDescription = "project_description";
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import 'package:mason/mason.dart';
import 'package:static_shock_cli/src/templates/blog_template.dart';
import 'package:static_shock_cli/src/templates/docs_multi_page_template.dart';
import 'package:static_shock_cli/src/templates/empty_template.dart';
import 'package:static_shock_cli/src/templates/tailwind_template.dart';

class TemplateCommand extends Command {
TemplateCommand(Logger log) {
addSubcommand(EmptyTemplateCommand(log));
addSubcommand(BlogTemplateCommand(log));
addSubcommand(DocsMultiPageTemplateCommand(log));
addSubcommand(TailwindTemplateCommand(log));
}

@override
Expand Down
15 changes: 15 additions & 0 deletions template_sources/template_tailwind/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Build output
/build

# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/

# Android Studio and IntelliJ IDEA
.idea

.DS_Store

# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock
30 changes: 30 additions & 0 deletions template_sources/template_tailwind/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.

include: package:lints/recommended.yaml

# Uncomment the following section to specify additional rules.

# linter:
# rules:
# - camel_case_types

# analyzer:
# exclude:
# - path/to/excluded/files/**

# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints

# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options
Binary file not shown.
Binary file not shown.
40 changes: 40 additions & 0 deletions template_sources/template_tailwind/bin/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'dart:io';

import 'package:static_shock/static_shock.dart';

Future<void> main(List<String> arguments) async {
var operatingSystem = Platform.operatingSystem;
String tailwindPath = switch (operatingSystem) {
"macos" => "./bin/macos/tailwindcss",
"linux" => "./bin/linux/tailwindcss",
"windows" => "./bin/windows/tailwindcss.exe",
_ => throw UnsupportedError(
"Unsupported operating system: $operatingSystem",
),
};

// Configure the static website generator.
final staticShock = StaticShock()
// Here, you can directly hook into the StaticShock pipeline. For example,
// you can copy an "images" directory from the source set to build set:
..pick(ExtensionPicker("html"))
..pick(DirectoryPicker.parse("images"))
// All 3rd party behavior is added through plugins, even the behavior
// shipped with Static Shock.
..plugin(const MarkdownPlugin())
..plugin(const JinjaPlugin())
..plugin(const PrettyUrlsPlugin())
..plugin(const RedirectsPlugin())
..plugin(const SassPlugin())
..plugin(DraftingPlugin(
showDrafts: arguments.contains("preview"),
))
..plugin(TailwindPlugin(
input: "source/styles/tailwind.css",
output: "build/styles/tailwind.css",
tailwindPath: tailwindPath,
));

// Generate the static website.
await staticShock.generateSite();
}
Binary file not shown.
14 changes: 14 additions & 0 deletions template_sources/template_tailwind/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: shock_template_tailwind
description:
version: 1.0.0
publish_to: none

environment:
sdk: ^3.0.0

dependencies:
static_shock: any

dev_dependencies:
lints: ^2.0.0
test: ^1.21.0
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/images/favicon/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading