Guide: Codestyle - dsriseah/ursys GitHub Wiki

STATUS: COLLECTING

General Conventions

  • top-level functions use function, but internal event-related function handlers are declared as arrow functions (this binds this implicitly, which is good for event handlers)




Generic Module Style

Note

Our VSCODE environments usually define this as a "snippet" named "module example" Use CMD-SHIFT-P "Insert Snippet" to find it

/*///////////////////////////////// ABOUT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*\

  Example of URSYS Module Style (2024)
  - Designed with C#-like lettercasing conventions, using comment formatting
    chosen for appropriate typographic visual weight and hierarchy
  - Assumes ES6 modules and Typescript (TS), but can be adapted for CJS
  - Uses JSDoc. If TS, description only. If JS, use @param, @returns, etc.

  See https://github.com/dsriseah/ursys/wiki/Guide:-Codestyle for more info.

\*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * /////////////////////////////////////*/

import * as UR from '@ursys/core'; // import UR module
import * as MODULE from './my-module'; // import all exports
import { SomeFunction } from './another-module'; // named export
import SomeClass from './yet-another-module'; // default export
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
type TSomeUnion = 'A' | 'B'; // this will be an exported type, note prefix

/// TYPE DECLARATIONS /////////////////////////////////////////////////////////
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
import type {
  TSomeObject, // exported types begin with T or I
  ISomeInterface
} from './file-with-exported-types.ts';
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
type LocalDefinedType = any; // local types don't begin with T or I
type AnotherLocalDefinedType = any;

/// CONSTANTS & DECLARATIONS //////////////////////////////////////////////////
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const TYPE_CONSTANT_A = 'A'; // constants are uppercase with at least 1 underscore
let m_index = 0; // module vars begin with m_ and are snake_case
let m_instance: MyClass; // begin with m_ and are snake_case
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/// module-wide collections example
const FOO_MAP: Map<any, any> = new Map(); // uppercase with at least 1 underscore

/// MODULE HELPERS /////////////////////////////////////////////////////////////
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/** HELPER: jsdoc short description
 *  indent to align with top
 */
function m_Method() {
  // helpers use the m_ prefix + PascalCase
  // they are general-purpose local functions that may have side effects
  // to implement logic that is shared across multiple functions
  return `hello ${m_index++}`;
}

/// UTILITY FUNCTIONS /////////////////////////////////////////////////////////
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/** UTILITY: jsdoc short description
 *  indent to align with top
 */
function u_Random() {
  // utilities use the u_ prefix + PascalCase
  // they are short functions, usually pure with no side effects.

  // local arrow functions begin with u_ and are snake_case
  const u_random = () => Math.random();

  // calling the local utility function in a loop
  const arr = Array.from({ length: 10 }, u_random);
}

/** An Extended Callout Block ************************************************|

  For in-line documentation or longer explanations of key classes
  Just write stuff. See the codestyle guide for alternatives.

|*****************************************************************************/

/// PUBLIC METHODS ////////////////////////////////////////////////////////////
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/** API: jsdoc short description
 *  indented additional line(s)
 */
function PublicA() {
  // public methods use PascalCase
  // they are the main API entry points for the module
}
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/** API: jsdoc short description
 *  indented additional line(s)
 */
function PublicB() {}
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/** API: funcs that return promises use async prefix */
async function AwaitPublicC() {}

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*:
  NOTE: Class declarations are usually in their own file, but they can be
  included in the same file as the module if there is only one class to
  create a hybrid module + manager class pattern
:*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/

/// CLASS DECLARATION /////////////////////////////////////////////////////////
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/** jsdoc comment
 *  indent additional lines
 */
class MyClass implements ISomeInterface {
  /* class fields declaration */
  propertyZ: string; // class instance prop declaration

  constructor() {
    this.propertyZ = 'Z'; // class instance prop declaration
  }
  /** API: jsdoc short description */
  classMethod() {
    // use Javascript camelCase convention for class method names
    FOO_MAP.clear();
  }

  /** HELPER: jsdoc short description */
  _privateMethod() {
    // use underscore prefix for "private" methods that aren't
    // intended to be called from outside the class
  }

  /* static methods and properties */
  static StaticMethod() {}
  static STATIC_CONSTANT = 'foo';
  static staticProperty = 'bar';
}

/// URSYS INTERFACES //////////////////////////////////////////////////////////
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
UR.HookPhase('MACHINE', 'PHASE', (...args) => {
  // hooks are used to tap into the named lifecycle
  // see URSYS documentation for more info
});
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
UR.HandleMessage('CHANNEL:MESSAGE', data => {
  // message handlers receive async data from a named channel
  // see URSYS documentation for more info
  return { ...data, status: 'OK' };
});

/// INITIALIZATION ////////////////////////////////////////////////////////////
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/// any code that runs when module is loaded
(function () {
  // use immediately-invoked function express (IIFE) for scope safety
  m_instance = new MyClass(); // instantiate class
  MyClass.StaticMethod(); // call static method
})();
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/// asynchroneous initialization version
(async function () {
  await AwaitPublicC(); // call async method
})();

/// EXPORTS ///////////////////////////////////////////////////////////////////
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
export default MyClass; // one class per file as default export
export {
  // group separate exports by type
  PublicA, // terse description of exported API method
  PublicB
};
export type {
  TSomeUnion // terse description of exported type
};




React Functional Components

See Using React with URSYS for additional info

Naming Patterns

  • declaration function MyComponent(props){}
  • internal helper function c_MyComponentHelper
  • internal UR message handler function const urmsg_MessageFunction = data => {}
  • internal UR lifecycle hook function const urstate_StateChangeFunction = state => {}
  • internal UI element const MyButton = ( <>jsx</> )
  • internal UI event handler function const evt_ElementUIActionName = evt => {}

Ordering within Functional Component

  1. state declaration
  2. effect hook declaration, return cleanup function for effect hook, other parameters
  3. UR hook and message handlers in useEffect() declarations
  4. declarations of internal helper functions
  5. declaration of event handlers
  6. declaration of JSX elements
  7. return of render function

Commenting

  • top of file in ABOUT area, briefly note the component context (e.g. parent component) and a USER-FOCUSED description of what it does.
  • use jsdoc to annotate functions, using the dashed-divider to separate each declared function for ease of scanning
  • use /** comment **/ as the subheaders style in the functional component to separate in this orderThere should be a space above and below it.
  • tersely document conditions and what they are used to change in conditional rendering




Typescript Conventions

  • get rid of jsdoc? Yes, but keep descriptions. typescript will also read jsdoc param too so it uses it, but as you add typedefs you can start removing them
  • object parameter with an arbitrary number of fields? { [key:string]:any } is for wildcard
  • should I use the data object as passing parameters or parameters? Use data param objects, but can use multiple params if the function name indicates (eg instead of AddComment(data) could use AddCommentById(id,data). Then immediately destructure (const { a, b } = data ) at the top of the function to make it obvious what is in the data packet right next to the signature
  • uiRefs are new nomenclature for identifying specific UI elements - there are two main perspectives, data-focused versus ui target focused, so make a table
  • for (NetCreate) CTYPE, change template string def ('foo' | 'bar') to string because these will be templated as arbitrary things in the future? For now, leave it as static strings; defer changing to a template-ready type for the future after everything else is typedef'd. Stage optimization for later.




Autoformatting

EditorConfig Setting .editorconfig (2024)

# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs

# Make sure your editor has a supported plugin that will
# automatically set according to this file
# http://editorconfig.org/#download

# stop searching for editorconfig in this file
root = true

# settings for our default files
[*.{js,jsx,txt,json,md}]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

# trailing whitespace means something in markdown
[*.md]
trim_trailing_whitespace = false

Prettier Settings .prettier.rc (2024)

module.exports = {
  semi: true,
  printWidth: 86,
  singleQuote: true,
  quoteProps: 'preserve',
  arrowParens: 'avoid',
  trailingComma: 'none'
};

⚠️ **GitHub.com Fallback** ⚠️