Modules
Modules allow code reuse and sharing, while protecting the IP of creators.
Directory structure
In your codebase, the modules
directory needs to be at the same level as the app
directory.
Module code can be split into 2 directories to protect IP (Intellectual Property). To create a module, split module code into public
and private
folders, and place all that code into the modules/MODULE_NAME
directory.
app
modules
└── MODULE_NAME
├── public
│ └── assets
└── private
└─ assets
public
and private
directories have the same structure as the standard app
folder, but if developers try to download or preview files after the module has been deployed to an Instance, they will only be able to access the files from the public
folder.
In general, when referencing files that are part of a module, platformOS uses a prefix which does not include any public
or private
folders (for example: modules/admin/app.css
instead of modules/admin/private/app.css
). This means that creators should not create files with the same name in both folders, as one will overwrite the other. This behavior gives creators flexibility: if they want to change the scope of the file, they can move it between the private and public folders without changing the code.
You can create an Instance and deploy the code as you would usually do, and you still can have the app
folder on the top level (this code will not be part of the module but you can use it while developing).
This mechanism allows creators to share their module code, make it configurable (by code in the public
folder), but still protect the IP in the private
folder.
Example: Module directories
An example of module code split into two directories, inside the modules
directory:
admincms
├── private
│ └── graphql
│ ├── get_records.graphql
│ └── get_pages.graphql
└── public
└── views
└── pages
└── admin.liquid
Namespacing rules
Configuration files placed in a module are treated a bit differently than regular files.
To avoid conflicts between the code of a module and regular code (or between modules) a namespacing strategy is used.
The general rule of accessing files in modules is to prefix paths with modules/MODULE_NAME
. Keep in mind that skipping resource type applies just like it does without modules. This means that when you access a partial
, you type a path to it as if you were in the partials directory.
When you use app: app/views/partials/users/settings/name.liquid
Is accessed by path: users/settings/name.liquid
When you use module: modules/admin/public/views/partials/users/settings/name.liquid
Is accessed by path: modules/admin/users/settings/name.liquid
The only difference is the prefix.
Examples
Assets
You can access assets placed at assets
directory in a module using asset_url filter.
Paths for those files are prefixed with modules/MODULE_NAME
modules/admin/public/assets/app.css
<link rel="stylesheet" media="screen" href="{{ 'modules/admin/app.css' | asset_url }}">
modules/admin/public/assets/admin.js
<script src="{{ 'modules/admin/admin.js' | asset_url }}"></script>
Partials
Partials can be referenced with their shorter name - the same as the regular ones.
For example, to include partial saved as modules/admin/private/views/partials/comments.liquid
, you should use:
{% include 'modules/admin/comments' %}
Layouts
Layout name has the same prefix when referenced within a page:
modules/admin/public/views/layouts/settings.liquid
slug: admin/settings
layout: modules/admin/settings
Records
modules/admin/private/records/resume.yml
query get_resumes {
records(
per_page: 5
filter: {
table: { value: "modules/admin/resume" }
}
) {
results {
id
}
}
}
Authorization Policies
modules/admin/public/authorization_policies/can_view_blog_posts.liquid
---
name: can_view_blog_posts
redirect_to: /blog
flash_alert: Please log in to access this page.
---
true
Within page/form it is referenced with a prefix:
authorization_policies:
- modules/admin/can_view_blog_posts
graphql Query
Query looks like any other query:
modules/admin/public/graphql/get_blog_instance.graphql
query get_blog_instance(
$current_user_id: ID
$slug: String
$scope: String
) {
records(
...
) {
}
But it is referenced with the prefix:
{%- graphql bi = 'modules/admin/get_blog_instance' -%}
Translations
Translations YML files for module should be placed in:
modules/admin/public/translations/
But when used in the module code they should be prefixed:
{{ 'modules/admin/strings.sample_string' | t }}