Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat): Generate scaffold in cli #12298

Merged
merged 53 commits into from
Nov 21, 2022
Merged

(feat): Generate scaffold in cli #12298

merged 53 commits into from
Nov 21, 2022

Conversation

Grubba27
Copy link
Contributor

@Grubba27 Grubba27 commented Nov 7, 2022

In this PR I focus on the creation of a new CLI command called generate, that will for the app user generate a scaffold API for him, this gives to the end user a little more time and a better DX since, he will be not needing to write this code that almost every MeteorJS RPC API has:
here is an example of usage. (in a simple js app)

meteor generate customer

it will generate the following code in /imports/api
Screenshot 2022-11-09 at 11 28 29

that will have the following code:

collection.js

 import { Mongo } from 'meteor/mongo';

export const CustomerCollection = new Mongo.Collection('customer');

methods.js

import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { CustomerCollection } from './collection';

export function create(data) {
  return CustomerCollection.insertAsync({ ...data });
}

export function update(_id, data) {
  check(_id, String);
  return CustomerCollection.updateAsync(_id, { ...data });
}

export function remove(_id) {
  check(_id, String);
  return CustomerCollection.removeAsync(_id);
}

export function findById(_id) {
  check(_id, String);
  return CustomerCollection.findOneAsync(_id);
}

Meteor.methods({
  'Customer.create': create,
  'Customer.update': update,
  'Customer.remove': remove,
  'Customer.find': findById
});

publication.js

import { Meteor } from 'meteor/meteor';
import { CustomerCollection } from './collection';

Meteor.publish('CustomersByLoggedUser', function publishCustomersByUserId() {
  return CustomerCollection.find({ userId: this.userId });
});

Meteor.publish('allCustomers', function publishCustomers() {
  return CustomerCollection.find({});
});

index.js

export * from './collection';
export * from './methods';
export * from './publications';

Also, there is the same version of these methods using TypeScript.

Path option

for those that may want to create in another path, you can use the --path option in order to select where to place this boilerplate.
It will generate the model in that path.

meteor generate another-customer --path=server/admin

it will generate in server/admin the another-client code:

Screenshot 2022-11-09 at 11 32 39

collection.ts

import { Mongo } from 'meteor/mongo';

export type AnotherCustomer = {
  _id?: string;
  name: string;
  createdAt: Date;
}

export const AnotherCustomerCollection = new Mongo.Collection<AnotherCustomer, AnotherCustomer>('another-customer');

methods.ts

import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import { AnotherCustomer, AnotherCustomerCollection } from './collection';

export function create(data: AnotherCustomer) {
  return AnotherCustomerCollection.insertAsync({ ...data });
}

export function update(_id: string, data: Mongo.Modifier<AnotherCustomer>) {
  check(_id, String);
  return AnotherCustomerCollection.updateAsync(_id, { ...data });
}

export function remove(_id: string) {
  check(_id, String);
  return AnotherCustomerCollection.removeAsync(_id);
}

export function findById(_id: string) {
  check(_id, String);
  return AnotherCustomerCollection.findOneAsync(_id);
}

Meteor.methods({
  'AnotherCustomer.create': create,
  'AnotherCustomer.update': update,
  'AnotherCustomer.remove': remove,
  'AnotherCustomer.find': findById
});

publications.ts

import { Meteor } from 'meteor/meteor';
import { AnotherCustomerCollection } from './collection';

Meteor.publish('AnotherCustomersByLoggedUser', function publishAnotherCustomersByUserId(this) {
  return AnotherCustomerCollection.find({ userId: this.userId });
});

Meteor.publish('allAnotherCustomers', function publishAnotherCustomers() {
  return AnotherCustomerCollection.find({});
});

index.ts

export * from './collection';
export * from './methods';
export * from './publications';

Using the Wizard

if you run the following command:

meteor generate

it will prompt the following questions. I've answered them enter for all of them

Screenshot 2022-11-09 at 11 38 29


Using your own template

Screenshot 2022-11-09 at 11 42 47

Note that I'm not trying to create a CLI framework inside meteor but just giving some solutions for really common problems out of the box

You can use your own templates for scaffolding your specific workloads. To do that, you should pass in a template directory URL so that it can copy it with its changes.

how do I rename things?

Out of the box I provide a few functions such as replacing $$name$$, $$PascalName$$ and $$camelName$$

these replacements come from this function:

const transformName = (name) => {
    return name.replace(/\$\$name\$\$|\$\$PascalName\$\$|\$\$camelName\$\$/g, function (substring, args) {
      if (substring === '$$name$$') return scaffoldName;
      if (substring === '$$PascalName$$') return toPascalCase(scaffoldName);
      if (substring === '$$camelName$$') return toCamelCase(scaffoldName);
    })
  }

What if I want to have my own way of templating?

There is an option called --replaceFn that when you pass in given a .js file with two functions it will override all templating that we have defaulted to use your given function.
example of a replacer file

export function transformFilename(scaffoldName, filename) {
  console.log(scaffoldName, filename);
  return filename
}

export function transformContents(scaffoldName, contents, fileName) {
  console.log(fileName, contents);
  return contents
}

if you run your command like this:

 meteor generate feed --replaceFn=/fn/replace.js

it will generate files full of $$PascalCase$$using the meteor provided templates.

A better example of this feature would be the following js file (like we do inside the cli):

const toPascalCase = (str) => {
  if(!str.includes('-')) return str.charAt(0).toUpperCase() + str.slice(1);
  else return str.split('-').map(toPascalCase).join('');
}
const toCamelCase = (str) => {
  if(!str.includes('-')) return str.charAt(0).toLowerCase() + str.slice(1);
  else return str.split('-').map(toPascalCase).join('');
}

const transformName = (scaffoldName, str) => {
    return str.replace(/\$\$name\$\$|\$\$PascalName\$\$|\$\$camelName\$\$/g, function (substring, args) {
      if (substring === '$$name$$') return scaffoldName;
      if (substring === '$$PascalName$$') return toPascalCase(scaffoldName);
      if (substring === '$$camelName$$') return toCamelCase(scaffoldName);
    })

}

export function transformFilename(scaffoldName, filename) {
  return transformName(scaffoldName, filename);
}

export function transformContents(scaffoldName, contents, fileName) {
  return transformName(scaffoldName, contents);
}

@Grubba27 Grubba27 added this to the Release 2.9 milestone Nov 7, 2022
@Grubba27 Grubba27 requested a review from denihs November 7, 2022 13:41
@Grubba27 Grubba27 self-assigned this Nov 7, 2022
tools/static-assets/scaffolds-js/index.js Outdated Show resolved Hide resolved
tools/static-assets/scaffolds-js/index.js Outdated Show resolved Hide resolved
tools/static-assets/scaffolds-js/index.js Outdated Show resolved Hide resolved
@Grubba27
Copy link
Contributor Author

Grubba27 commented Nov 9, 2022

Hey @StorytellerCZ check if those changes that I've made makes sense to you
I've written in the pr description the general idea, and this will probably be the docs when this goes live.
I think the API naming is still up for debate, calling it by replaceFn sounds good but I'm not sure

@github-actions github-actions bot temporarily deployed to pull request November 10, 2022 17:41 Inactive
@github-actions github-actions bot temporarily deployed to pull request November 10, 2022 20:12 Inactive
@github-actions github-actions bot temporarily deployed to pull request November 10, 2022 20:33 Inactive
Copy link
Collaborator

@StorytellerCZ StorytellerCZ left a comment

Choose a reason for hiding this comment

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

I think these are good initial steps.

@github-actions github-actions bot temporarily deployed to pull request November 14, 2022 17:04 Inactive
@Grubba27 Grubba27 changed the base branch from release-2.8.1 to release-2.9 November 21, 2022 14:17
@Grubba27 Grubba27 merged commit 85c0bd5 into release-2.9 Nov 21, 2022
@Grubba27 Grubba27 mentioned this pull request Nov 30, 2022
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants