Skip to content

Commit

Permalink
extract moss_helpers.ts and lazy load the Moss plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanatkn committed Oct 31, 2024
1 parent 256f68a commit 9caa4a8
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 100 deletions.
5 changes: 5 additions & 0 deletions .changeset/nice-garlics-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ryanatkn/gro': minor
---

extract `moss_helpers.ts` and lazy load the Moss plugin
24 changes: 12 additions & 12 deletions package-lock.json

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

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,17 @@
"@changesets/types": "^6.0.0",
"@ryanatkn/eslint-config": "^0.5.6",
"@ryanatkn/fuz": "^0.130.3",
"@ryanatkn/moss": "^0.19.0",
"@ryanatkn/moss": "^0.20.0",
"@sveltejs/adapter-static": "^3.0.6",
"@sveltejs/kit": "^2.7.3",
"@sveltejs/package": "^2.3.7",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"@types/fs-extra": "^11.0.4",
"@types/node": "^22.8.4",
"@types/node": "^22.8.5",
"esbuild": "^0.21.5",
"eslint": "^9.13.0",
"eslint-plugin-svelte": "^2.46.0",
"svelte": "^5.1.5",
"svelte": "^5.1.6",
"svelte-check": "^4.0.5",
"typescript": "^5.6.3",
"typescript-eslint": "^8.12.2",
Expand Down
25 changes: 10 additions & 15 deletions src/lib/gro.config.default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import {has_server, gro_plugin_server} from './gro_plugin_server.js';
import {gro_plugin_sveltekit_app} from './gro_plugin_sveltekit_app.js';
import {has_sveltekit_app, has_sveltekit_library} from './sveltekit_helpers.js';
import {gro_plugin_gen} from './gro_plugin_gen.js';
import {gro_plugin_moss, has_moss_dep} from './gro_plugin_moss.js';
import {load_package_json} from './package_json.js';
import {has_dep, load_package_json} from './package_json.js';

/**
* This is the default config that's passed to `gro.config.ts`
Expand All @@ -19,22 +18,18 @@ import {load_package_json} from './package_json.js';
const config: Create_Gro_Config = async (cfg) => {
const package_json = load_package_json(); // TODO gets wastefully loaded by some plugins, maybe put in plugin/task context? how does that interact with `map_package_json`?

const [
moss_plugin_result,
has_server_result,
has_sveltekit_library_result,
has_sveltekit_app_result,
] = await Promise.all([
has_moss_dep(package_json),
has_server(),
has_sveltekit_library(package_json),
has_sveltekit_app(),
]);
const [has_moss_dep, has_server_result, has_sveltekit_library_result, has_sveltekit_app_result] =
await Promise.all([
has_dep('@ryanatkn/moss', package_json),
has_server(),
has_sveltekit_library(package_json),
has_sveltekit_app(),
]);

cfg.plugins = () =>
cfg.plugins = async () =>
[
// put things that generate files before SvelteKit so it can see them
moss_plugin_result.ok ? gro_plugin_moss() : null,
has_moss_dep ? (await import('./gro_plugin_moss.js')).gro_plugin_moss() : null, // lazy load to avoid errors if it's not installed
gro_plugin_gen(),
has_server_result.ok ? gro_plugin_server() : null,
has_sveltekit_library_result.ok ? gro_plugin_sveltekit_library() : null,
Expand Down
51 changes: 8 additions & 43 deletions src/lib/gro_plugin_moss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,26 @@ import {EMPTY_OBJECT} from '@ryanatkn/belt/object.js';
import {throttle} from '@ryanatkn/belt/throttle.js';
import {Unreachable_Error} from '@ryanatkn/belt/error.js';
import {writeFileSync} from 'node:fs';
import {collect_css_classes, Css_Classes} from '@ryanatkn/moss/css_class_helpers.js';
import {
collect_css_classes,
Css_Classes,
generate_classes_css,
} from '@ryanatkn/moss/css_class_helpers.js';
import {css_classes_by_name} from '@ryanatkn/moss/css_classes.js';
import type {Result} from '@ryanatkn/belt/result.js';

import type {Plugin} from './plugin.js';
import type {Args} from './args.js';
import type {Cleanup_Watch} from './filer.js';
import {format_file} from './format_file.js';
import type {File_Filter} from './path.js';
import {has_dep, type Package_Json} from './package_json.js';

export const MOSS_PACKAGE_DEP_NAME = '@ryanatkn/moss';

export const has_moss_dep = (
package_json: Package_Json,
dep_name = MOSS_PACKAGE_DEP_NAME,
): Result<object, {message: string}> => {
if (!has_dep(dep_name, package_json)) {
return {
ok: false,
message: `no dependency found in package.json for ${dep_name}`,
};
}

return {ok: true};
};

export const generate_classes_css = (classes: Iterable<string>): string => {
let css = '';
for (const c of classes) {
const v = css_classes_by_name[c];
if (!v) {
// diagnostic
// if (!/^[a-z_0-9]+$/.test(c)) {
// console.error('invalid class detected, fix the regexps', c);
// }
continue;
}
if ('declaration' in v) {
css += `.${c} { ${v.declaration} }\n`;
} else {
css += v.ruleset + '\n';
}
}

return css;
};

const FLUSH_DEBOUNCE_DELAY = 500;

export interface Task_Args extends Args {
watch?: boolean;
}

export interface Options {
export interface Gro_Plugin_Moss_Options {
include_classes?: string[] | Set<string> | null;
outfile?: string;
filter_file?: File_Filter | null;
Expand All @@ -70,7 +35,7 @@ export const gro_plugin_moss = ({
filter_file = (p) => !p.includes('.test.') && !p.includes('/test/'),
flush_debounce_delay = FLUSH_DEBOUNCE_DELAY,
banner = 'generated by gro_plugin_moss',
}: Options = EMPTY_OBJECT): Plugin => {
}: Gro_Plugin_Moss_Options = EMPTY_OBJECT): Plugin => {
const css_classes = new Css_Classes(
Array.isArray(include_classes) ? new Set(include_classes) : include_classes,
);
Expand All @@ -88,7 +53,7 @@ export const gro_plugin_moss = ({
};
const flush_gen_queue = throttle(
async () => {
const css = generate_classes_css(css_classes.get_sorted_array());
const css = generate_classes_css(css_classes.get_sorted_array(), css_classes_by_name);
const contents = `/* ${banner} */\n\n${css}\n\n/* ${banner} */`;
const output = await format_file(contents, {filepath: outfile});
// TODO think about using gen to implement this, would have some nice benefits like automatic change detection
Expand Down
11 changes: 4 additions & 7 deletions src/lib/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,17 @@ export const package_json = {
'@changesets/types': '^6.0.0',
'@ryanatkn/eslint-config': '^0.5.6',
'@ryanatkn/fuz': '^0.130.3',
'@ryanatkn/moss': '^0.19.0',
'@ryanatkn/moss': '^0.20.0',
'@sveltejs/adapter-static': '^3.0.6',
'@sveltejs/kit': '^2.7.3',
'@sveltejs/package': '^2.3.7',
'@sveltejs/vite-plugin-svelte': '^4.0.0',
'@types/fs-extra': '^11.0.4',
'@types/node': '^22.8.4',
'@types/node': '^22.8.5',
esbuild: '^0.21.5',
eslint: '^9.13.0',
'eslint-plugin-svelte': '^2.46.0',
svelte: '^5.1.5',
svelte: '^5.1.6',
'svelte-check': '^4.0.5',
typescript: '^5.6.3',
'typescript-eslint': '^8.12.2',
Expand Down Expand Up @@ -605,11 +605,8 @@ export const src_json = {
'./gro_plugin_moss.js': {
path: 'gro_plugin_moss.ts',
declarations: [
{name: 'MOSS_PACKAGE_DEP_NAME', kind: 'variable'},
{name: 'has_moss_dep', kind: 'function'},
{name: 'generate_classes_css', kind: 'function'},
{name: 'Task_Args', kind: 'type'},
{name: 'Options', kind: 'type'},
{name: 'Gro_Plugin_Moss_Options', kind: 'type'},
{name: 'gro_plugin_moss', kind: 'function'},
],
},
Expand Down
51 changes: 31 additions & 20 deletions src/lib/resolve_node_specifier.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import {join, extname} from 'node:path';
import {existsSync} from 'node:fs';
import {DEV} from 'esm-env';
import {escape_regexp} from '@ryanatkn/belt/regexp.js';

import {Export_Value, Package_Json, load_package_json} from './package_json.js';
import {paths} from './paths.js';
import {NODE_MODULES_DIRNAME} from './constants.js';
import type {Resolved_Specifier} from './resolve_specifier.js';
import {escape_regexp} from '@ryanatkn/belt/regexp.js';

/**
* This likely has differences from Node - they should be fixed on a case-by-case basis.
* Ideally Gro would just use `import.meta.resolve`, but it can't be used in custom loaders,
* which Gro relies on for TypeScript.
* This likely has differences from Node - they should be fixed on a case-by-case basis.
*/
export const resolve_node_specifier = (
specifier: string,
Expand All @@ -24,23 +24,16 @@ export const resolve_node_specifier = (
const raw = specifier.endsWith('?raw');
const mapped_specifier = raw ? specifier.substring(0, specifier.length - 4) : specifier;

// Parse the specifier
let idx: number = -1;
if (mapped_specifier[0] === '@') {
let count = 0;
for (let i = 0; i < mapped_specifier.length; i++) {
if (mapped_specifier[i] === '/') count++;
if (count === 2) {
idx = i;
break;
}
}
} else {
idx = mapped_specifier.indexOf('/');
}
const specifier_slash_path_index = get_specifier_slash_path_index(mapped_specifier);

const pkg_name = idx === -1 ? mapped_specifier : mapped_specifier.substring(0, idx);
const module_path = idx === -1 ? '' : mapped_specifier.substring(idx + 1);
const pkg_name =
specifier_slash_path_index === -1
? mapped_specifier
: mapped_specifier.substring(0, specifier_slash_path_index);
const module_path =
specifier_slash_path_index === -1
? ''
: mapped_specifier.substring(specifier_slash_path_index + 1);
const subpath = module_path ? './' + module_path : '.';
const package_dir = join(dir, NODE_MODULES_DIRNAME, pkg_name);

Expand Down Expand Up @@ -146,6 +139,7 @@ const resolve_subpath = (package_json: Package_Json, subpath: string): unknown =
return exports[subpath];
}

// TODO some of this may be wrong, will just need to patch as we go
// Sort patterns by specificity
const patterns = Object.entries(exports)
.filter(([pattern]) => pattern.includes('*'))
Expand Down Expand Up @@ -257,8 +251,7 @@ const resolve_exported_value = (

const exported_obj = exported as Record<string, unknown>;

// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
let default_value: Export_Value | undefined;
let default_value: Export_Value | undefined; // eslint-disable-line @typescript-eslint/no-redundant-type-constituents

// For each key in exported_obj, in order
for (const [condition, value] of Object.entries(exported_obj)) {
Expand Down Expand Up @@ -356,3 +349,21 @@ const validate_export_target = (target: string, throw_on_missing_package: boolea
}
}
};

const get_specifier_slash_path_index = (mapped_specifier: string): number => {
let index: number = -1;
if (mapped_specifier[0] === '@') {
let count = 0;
for (let i = 0; i < mapped_specifier.length; i++) {
if (mapped_specifier[i] === '/') count++;
if (count === 2) {
index = i;
break;
}
}
} else {
index = mapped_specifier.indexOf('/');
}

return index;
};

0 comments on commit 9caa4a8

Please sign in to comment.