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: cache tokens #508

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
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
16 changes: 14 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/muon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
"glob-to-regexp": "0.4.1",
"lit": "2.7.3",
"lodash": "4.17.21",
"node-cache": "^5.1.2",
"object-hash": "^3.0.0",
"path-is-inside": "1.0.2",
"postcss": "8.4.23",
"postcss-clean": "1.2.2",
Expand Down
155 changes: 155 additions & 0 deletions packages/muon/scripts/utils/cache.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import fs from 'node:fs';
import NodeCache from 'node-cache';
import path from 'node:path';


export default class Cache {


/**
* @property {NodeCache} cacheObj The cache object using Node package.
*/
#cacheObj = {};


/**
* @property {object} options The options.
*/
#options = {};


/**
*
* @param {object} options Accept arguments.
* @param {string} options.cacheJsonPath The cache JSON file full path. This file will be use for persistent cache across process end/start.
* @param {object} options.cacheOptions The cache options. Currently use node-cache, see https://www.npmjs.com/package/node-cache.
*/
constructor({ } = {}) {
const defaults = {
cacheJsonPath: path.resolve('node_modules/.cache/muon/cache.json'),
cacheOptions: {},
}

this.#options = {
...defaults,
...arguments[0]
}

this.#prepareCacheFolder();

this.#cacheObj = new NodeCache(this.#options.cacheOptions);
this.#restoreCacheFile();
this.#listenEvents();
}// constructor


/**
* Listen cache class events to work.
*
* @private This method was called from `constructor()`.
*/
#listenEvents() {
this.#cacheObj.on('set', (key, value) => {
this.#writeAllCacheData(this.#cacheObj.data);
});

this.#cacheObj.on('del', (key, value) => {
this.#writeAllCacheData(this.#cacheObj.data);
});

this.#cacheObj.on('expired', (key, value) => {
this.#writeAllCacheData(this.#cacheObj.data);
});

this.#cacheObj.on('flush', () => {
this.#writeAllCacheData(this.#cacheObj.data);
});
}// listenEvents


/**
* Prepare cache JSON file.
*
* @private This method was called from `#prepareCacheFolder()`.
*/
#prepareCacheFile() {
const content = {};

this.#writeAllCacheData(content);
}// prepareCacheFile


/**
* Prepare cache folder where JSON cache file will be store.
*
* @private This method was called from `constructor()`.
* @returns {void}
*/
#prepareCacheFolder() {
if (fs.existsSync(this.#options.cacheJsonPath)) {
// if file is already exists.
// do nothing.
return;
}

const cacheDir = path.dirname(this.#options.cacheJsonPath);
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir, { recursive: true });
this.#prepareCacheFile();
}

// check again.
if (!fs.existsSync(cacheDir)) {
// if still not exists.
throw new Error('The cache folder is not exists and could not be created. (' + cacheDir + ')');
}
}// prepareCacheFolder


/**
* Restore cache file.
*
* @private This method was called from `constructor()`.
*/
#restoreCacheFile() {
const JSONContent = fs.readFileSync(
this.#options.cacheJsonPath,
{
'encoding': 'utf8',
'flag': 'r',
}
);

this.#cacheObj.data = JSON.parse(JSONContent);
}// restoreCacheFile


/**
* Write all cache data to JSON file.
*
* @private This method was called from `#listenEvents()`, `#prepareCacheFile()`.
* @param {mixed} data The data to write.
*/
#writeAllCacheData(data) {
const dataJSON = JSON.stringify(data, undefined, 4);

fs.writeFileSync(
this.#options.cacheJsonPath,
dataJSON,
{
'encoding': 'utf8',
}
);
}// writeAllCacheData


/**
* Returns the cache object on `#cacheObj` property.
*
* @returns {object} Returns the cache object on `#cacheObj` property.
*/
cacheObj() {
return this.#cacheObj;
}// cacheObj

}
65 changes: 57 additions & 8 deletions packages/muon/scripts/utils/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import ts from 'typescript';
import { analyzeText, analyzeSourceFile, transformAnalyzerResult } from 'web-component-analyzer';
import StyleDictionary from 'style-dictionary';
import formatHelpers from 'style-dictionary/lib/common/formatHelpers/index.js';
import hash from 'object-hash';
import Cache from './cache.mjs';
import _ from 'lodash';
import appRoot from 'app-root-path';
import { glob } from 'glob';
Expand All @@ -23,7 +25,6 @@ const __dirname = path.dirname(__filename);
const cleanup = (destination, cleanOnRollup = false) => {
return new Promise((resolve) => {
const cemFilePath = path.join(destination, 'custom-elements.json');
const buildPath = path.join(__filename, '..', '..', '..', 'build');

if (fs.existsSync(destination)) {
if (cleanOnRollup) {
Expand All @@ -37,10 +38,6 @@ const cleanup = (destination, cleanOnRollup = false) => {
if (!fs.existsSync(destination)) {
fs.mkdirSync(destination);
}
if (!cleanOnRollup) {
fs.rmSync(buildPath, { force: true, recursive: true });
fs.mkdirSync(buildPath);
}
return resolve();
});
};
Expand Down Expand Up @@ -210,7 +207,7 @@ const sourceFilesAnalyzer = async () => {
}));

const tagNames = results?.map((result) => {
const {tagName, prefix } = getTagFromAnalyzerResult(result);
const { tagName, prefix } = getTagFromAnalyzerResult(result);

const elementName = `${prefix}-${tagName}`;
result.componentDefinitions[0].tagName = elementName;
Expand Down Expand Up @@ -278,10 +275,62 @@ const styleDictionary = async () => {
return styleDict;
};

const cacheTokens = async () => {
const config = getConfig();
const root = path.join(__filename, '..', '..', '..');
let sourceDir = [root + '/tokens/**/*.js', root + '/tokens/**/*.json', root + '/tokens/*.json', root + '/components/**/**/config-tokens.json', root + '/components/**/**/design-tokens.json'];
let hasDifferences = false;

if (config?.tokens?.dir) {
sourceDir = [
...sourceDir,
...config.tokens.dir
];
}

const sourceFiles = await glob.glob(sourceDir);
const cacheObj = new Cache();
const nodeCacheObj = cacheObj.cacheObj();

const promises = [];
for (const file of sourceFiles) {
const fileSource = fs.readFileSync(file, 'utf8');
const hashedFile = hash(fileSource);

if (hashedFile !== nodeCacheObj.get(file)) {
hasDifferences = true;
nodeCacheObj.set(file, hashedFile);
}

promises.push('true');
}

await Promise.all(promises);

return hasDifferences;
};

const clearTokens = async () => {
return new Promise((resolve) => {
const buildPath = path.join(__filename, '..', '..', '..', 'build');

fs.rmSync(buildPath, { force: true, recursive: true });
fs.mkdirSync(buildPath);

return resolve();
});
};

const createTokens = async () => {
const dictionary = await styleDictionary();
const hasDifferences = await cacheTokens();

return dictionary.buildAllPlatforms();
if (hasDifferences) {
await clearTokens();
const dictionary = await styleDictionary();
return dictionary.buildAllPlatforms();
} else {
return Promise.resolve();
}
};

const componentDefiner = async () => {
Expand Down