Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ee63a89
add examples module with constants and data validation
nachocodoner Mar 25, 2026
7e61143
add examples cache read/write
nachocodoner Mar 25, 2026
003c109
add getExamples orchestrator with cache-first strategy
nachocodoner Mar 25, 2026
4338bcf
add example resolution and clone utilities
nachocodoner Mar 25, 2026
b5888ea
add Meteor app validation for create command
nachocodoner Mar 25, 2026
e1085cb
wire commands.js to new examples module and remove old example code
nachocodoner Mar 25, 2026
21f97fa
add E2E test for meteor create --list and --example
nachocodoner Mar 25, 2026
a0ad998
enhance error reporting in CLI examples command and update examples b…
nachocodoner Mar 25, 2026
c65602e
prioritize network over cache for examples retrieval; add fallback me…
nachocodoner Mar 25, 2026
c27a42a
include Meteor version in CLI examples list
nachocodoner Mar 25, 2026
edc2873
refactor examples tests: replace `assert` with `expect` and switch to…
nachocodoner Mar 25, 2026
6309002
refactor `validateExamplesData` to support custom warning function vi…
nachocodoner Mar 25, 2026
1e9473f
remove unused `refresh` option from CLI commands
nachocodoner Mar 25, 2026
b29a15b
add support for `--from-branch` option in `meteor create` command
nachocodoner Mar 25, 2026
9ebb976
refine `meteor create` documentation: clarify options and add details…
nachocodoner Mar 25, 2026
f0505f6
add tests for `meteor create` edge cases with `--from` and `--from-di…
nachocodoner Mar 25, 2026
b06b4a8
update CLI docs to include enhanced example workflows and repository …
nachocodoner Mar 25, 2026
065eac8
enhance examples listing with improved formatting and additional deta…
nachocodoner Mar 26, 2026
33f33c9
update e2e test: adjust match text to "Meteor Examples" for consistency
nachocodoner Mar 26, 2026
2bb34d4
update examples cache: use tropohouse path and handle write failures …
nachocodoner Mar 26, 2026
806e2b2
adjust help text matching and refine `--list` option verification
nachocodoner Mar 26, 2026
a26a315
refactor examples repo cloning: replace `exec` with `execFile` for im…
nachocodoner Mar 26, 2026
88672b3
handle `--refresh` in examples fetch: avoid using stale cache and upd…
nachocodoner Mar 26, 2026
9f61ccb
validate subdirectory paths in examples fetch to prevent directory tr…
nachocodoner Mar 26, 2026
e3e5936
add "Examples" to workflow test matrix in e2e-tests
nachocodoner Mar 30, 2026
f7e639f
Merge branch 'release-3.4.1' into examples-revamp
nachocodoner Mar 30, 2026
a6a749b
Merge remote-tracking branch 'origin/examples-revamp' into examples-r…
nachocodoner Mar 30, 2026
5004a9e
improve examples documentation
nachocodoner Mar 31, 2026
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
Prev Previous commit
Next Next commit
wire commands.js to new examples module and remove old example code
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
  • Loading branch information
nachocodoner and claude committed Mar 25, 2026
commit e1085cb8e1a51cfcfcec134e2ee962f64df8a4cb
154 changes: 81 additions & 73 deletions tools/cli/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ import { ensureDevBundleDependencies } from '../cordova/index.js';
import { CordovaRunner } from '../cordova/runner.js';
import { iOSRunTarget, AndroidRunTarget } from '../cordova/run-targets.js';

import { EXAMPLE_REPOSITORIES } from './example-repositories.js';
import { getExamples, findExample, cloneRepo, cloneSubdirectory, validateMeteorApp, EXAMPLES_REPO, EXAMPLES_BRANCH } from './examples.js';

// The architecture used by Meteor Software's hosted servers; it's the
// architecture used by 'meteor deploy'.
Expand Down Expand Up @@ -675,17 +675,6 @@ main.registerCommand({
* Resolves into json with
* @returns {Promise<[Skeletons, null]> | Promise<[null, Error]>}
*/
function getExamplesJSON(){
return tryRun(async () => {
const response = await httpHelpers.request({
url: "https://cdn.meteor.com/static/meteor.json",
method: "GET",
useSessionHeader: true,
useAuthHeader: true,
});
return JSON.parse(response.body);
});
}

const DEFAULT_SKELETON = "react";
export const AVAILABLE_SKELETONS = [
Expand Down Expand Up @@ -751,6 +740,8 @@ main.registerCommand({
legacy: { type: Boolean },
prototype: { type: Boolean },
from: { type: String },
'from-dir': { type: String },
refresh: { type: Boolean },
},
pretty: false,
catalogRefresh: new catalog.Refresh.Never()
Expand All @@ -774,6 +765,11 @@ main.registerCommand({
Console.error();
throw new main.ShowUsage();
}
if (options.from || options['from-dir']) {
Console.error("Package creation does not support --from or --from-dir.");
Console.error();
throw new main.ShowUsage();
}

if (!packageName) {
Console.error("Please specify the name of the package.");
Expand Down Expand Up @@ -895,26 +891,28 @@ main.registerCommand({
}

if (options.list) {
Console.info("Available examples:");
const [json, err] = await getExamplesJSON()
if (err) {
Console.error("Failed to fetch examples:", err.message);
Console.info("Using cached examples.json");
}
const examples = err ? EXAMPLE_REPOSITORIES : json;
_.each(examples, function (repoInfo, name) {
const branchInfo = repoInfo.branch ? `/tree/${repoInfo.branch}` : "";
try {
const examples = await getExamples({ refresh: !!options.refresh });
Console.info('Available examples:');
Console.info();
examples.forEach(ex => {
Console.info(Console.command(ex.slug), Console.options({ indent: 2 }));
if (ex.why) {
Console.info(ex.why, Console.options({ indent: 4 }));
}
Console.info(ex.stack.join(', '), Console.options({ indent: 4 }));
const url = ex.repositoryUrl || `${EXAMPLES_REPO}/tree/${EXAMPLES_BRANCH}/${ex.internalPath}`;
Console.info(Console.url(url), Console.options({ indent: 4 }));
Console.info();
});
Console.info(
Console.command(`${name}: ${repoInfo.repo}${branchInfo}`),
Console.options({ indent: 2 })
'To create an example:',
Console.command("'meteor create <app-name> --example <name>'")
);
});

Console.info();
Console.info(
"To create an example, simply",
Console.command("'meteor create <app-name> --example <name>'")
);
} catch (err) {
Console.error(err.message);
return 1;
}
return 0;
}

Expand Down Expand Up @@ -1151,57 +1149,67 @@ main.registerCommand({

}

/**
*
* @param {string} url
*/
const setupExampleByURL = async (url) => {
const [ok, err] = await bash`git --version`;
if (err) throw new Error("git is not installed");
const isWindows = process.platform === "win32";

// Set GIT_TERMINAL_PROMPT=0 to disable prompting
process.env.GIT_TERMINAL_PROMPT = 0;

const gitCommand = isWindows
? `git clone --progress ${url} "${files.convertToOSPath(appPath)}"`
: `git clone --progress ${url} ${appPath}`;
const [okClone, errClone] = await bash`${gitCommand}`;
const errorMessage = errClone && typeof errClone === "string" ? errClone : errClone?.message;
if (errorMessage && errorMessage.includes("Cloning into")) {
throw new Error("error cloning skeleton");
}
// remove .git folder from the example
await files.rm_recursive_async(files.pathJoin(appPath, ".git"));
await setupMessages();
};

if (options.example) {
const [json, err] = await getExamplesJSON();
try {
let examples = await getExamples();
let example = findExample(examples, options.example);

if (err) {
Console.error("Failed to fetch examples:", err.message);
Console.info("Using cached examples.json");
}
if (!example) {
examples = await getExamples({ refresh: true });
example = findExample(examples, options.example);
}

const examples = err ? EXAMPLE_REPOSITORIES : json;
const repoInfo = examples[options.example];
if (!repoInfo) {
Console.error(`${options.example}: no such example.`);
Console.error(
"List available applications with",
Console.command("'meteor create --list'") + "."
);
if (!example) {
Console.error(`'${options.example}' is not a known example.`);
Console.error('Run', Console.command("'meteor create --list'"), 'to see available examples.');
return 1;
}

if (example.isInternal) {
await cloneSubdirectory(EXAMPLES_REPO, EXAMPLES_BRANCH, example.internalPath, appPath);
} else {
await cloneRepo(example.repositoryUrl, appPath);
}

await setupMessages();
} catch (err) {
Console.error('Error creating example:', err.message);
return 1;
}
// repoInfo.repo is the URL of the repo, and repoInfo.branch is the branch
await setupExampleByURL(repoInfo.repo);
return 0;
}

if (options['from-dir'] && !options.from) {
Console.error('--from-dir requires --from to specify the source repository.');
return 1;
}

if (options.from) {
await setupExampleByURL(options.from);
try {
if (options['from-dir']) {
let repoUrl = options.from;
try {
const examples = await getExamples();
const example = findExample(examples, options.from);
if (example) {
repoUrl = example.repositoryUrl || EXAMPLES_REPO;
}
} catch (e) {
// If examples fetch fails, treat --from as a URL
}

await cloneSubdirectory(repoUrl, null, options['from-dir'], appPath);
validateMeteorApp(appPath);
} else {
await cloneRepo(options.from, appPath);
validateMeteorApp(appPath);
}

await setupMessages();
} catch (err) {
Console.error(err.message);
return 1;
}
return 0;
}

Expand Down Expand Up @@ -1271,8 +1279,8 @@ main.registerCommand({
// using it as it was before 2.x
if (release.explicit) throw new Error("Using release option");

// If local skeleton doesn't exist, use setupExampleByURL
await setupExampleByURL(`https://github.com/meteor/skel-${skeleton}`);
// If local skeleton doesn't exist, clone from GitHub
await cloneRepo(`https://github.com/meteor/skel-${skeleton}`, appPath);
} catch (e) {
if (
e.message !== "Using prototype option" &&
Expand Down
14 changes: 0 additions & 14 deletions tools/cli/example-repositories.js

This file was deleted.